-
题目:
给定一个链表头节点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;
}
};
- 总结:
单链表中的删除操作:①常规遍历寻找前一个结点 ②狸猫换太子(但要求提供待删结点的指针)