剑指Offer18.删除链表的结点

  • 剑指Offer18.删除链表的结点

  • 题目:
    给定一个链表头节点head,和一个待删除节点值val;
    链表的每个节点值都不相同(意思就是说最多只需要删一个节点就好);

  • 思路:
    1.单指针:O(n):遍历找到目标节点的前一个结点,O(1):只需要一个虚拟头节点dummy和常数空间的指针pre
    在单链表中,设置一个pre指针方便删除目标结点;
    设置虚拟头节点dummy,方便删除头节点,返回的是dummy->next;

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* deleteNode(ListNode* head, int val) {
    
    
        if (!head) return nullptr;

        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* pre = dummy;p
        while (pre->next) {
    
    //去找到待删结点的前一个结点pre
            if (pre->next->val == val) pre->next = pre->next->next;//没有delete
            else pre = pre->next;
        }
        return dummy->next;
    }
};

2.狸猫换太子:平均O(1):最坏情况下即删除尾节点时O(n),其余情况均是O(1),因此平均下来[ (n-1) * O(1) + 1 * O(n) ] / n = O(1),O(1)
参数要求用ListNode*表示 待删结点,此题等同于面试题02.03.删除中间节点
本题有一个前提:待删结点pDelete真的在本链表内,但除非O(n)遍历一次才能确保这一点,若本题要求时间只能是O(1),就只能把这一前提交给调用者了;

class Solution {
    
    
public:
    ListNode* deleteNode(ListNode* head, ListNode* pDelete) {
    
    //注意参数不同,必须用指针表示待删结点,若用方法1的参数就无法使用此方法
        if (!head) return nullptr;//链表为空
        else if (!head->next) {
    
    //仅有一个节点
            if (head == pDelete) return nullptr; //该结点要被删除,就返回空
            else return head; //该节点不是待删结点,就原样返回
        }
        else if (pDelete->next) {
    
    //至少有两个结点, 若待删结点不是尾节点,狸猫换太子优化为O(1)
            pDelete->val = pDelete->next->val;
            pDelete->next = pDelete->next->next;//没有delete
        }
        else {
    
    //最坏情况O(n):待删结点恰好是尾节点,只能遍历寻找寻找尾节点的前一个,作常规删除
            ListNode* cur = head;
            while (cur->next != pDelete) {
    
    //寻找尾节点并删除
                cur = cur->next;
            }
            cur->next = nullptr;//没有delete
        }
        return head;//无需dummy,因为删除的一定不是head
    }
};
  • 题目2:
    删除链表中重复的节点
    若有序链表中存在重复节点,就将其全部删掉;(类似的题是将重复节点剩下一个,类似unique的操作)
  • 思路:
    有序链表代表相同值都是相邻的;

1.遍历一次:O(n),O(1)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
    
    
public:
    ListNode* deleteDuplication(ListNode* head) {
    
    
        if (!head) return nullptr;
        else if (!head->next) return head;
        
        auto dummy = new ListNode(-1);
        dummy->next = head;
        auto p = dummy;
        while (p->next) {
    
    //p:待检测区间的前一个结点
            auto q = p->next;//q:待检测区间的第一个节点
            while (q && p->next->val == q->val) q = q->next;//q最终停在待检测区间的下一节点
            if (p->next->next == q) p = p->next;//待检测区间长度为1
            else {
    
    //待删区间:[p->next, q)
                auto del = p->next;
                while (del != q) {
    
    
                    auto tmp = del->next;
                    delete del;
                    del = tmp;
                }
                p->next = q;
            }
        }
        return dummy->next;
    }
};
  • 总结:
    单链表中的删除操作:①常规遍历寻找前一个结点 ②狸猫换太子(但要求提供待删结点的指针)

猜你喜欢

转载自blog.csdn.net/jiuri1005/article/details/113839083