“ 在做链表相关题的时候,常常需要针对头节点单独考虑,但实际上对头节点进行处理的代码逻辑与非头节点的又特别地相似,此时通过在链表头节点前增加虚拟头节点,可以既使得代码更加优美又能避免对头节点得单独考虑。”
82. 删除排序链表中的重复元素 II
题意:删除排序链表中所有含有重复数字的节点,只保留原始链表中没有出现的数字。
解题思路
以链表 1->1->1->2->3 为栗子,删除值为 1 的节点。
删除前:
删除后:
在原链表的头节点前增加虚拟头节点:
定义两个指针 pre/cur,分别指向虚拟头节点和头节点
当 cur 指向的节点的值等于其下一个节点的值时,右移 cur 直到其指向的节点的值与其下一个节点的值不等
此时,将 pre 指向的节点指向 cur 指向的节点的下一个节点,即 pre->next = cur->next,相当于删除链表中所有节点值为 1 的节点
继续右移 cur,判断是否还有其指向的节点的值与其下一个节点值相等,同时右移 pre,直至 cur 指向链表尾节点
Show me the Code
// c++
ListNode* deleteDuplicates(ListNode* head) {
/* 创建虚拟头节点 */
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* pre = dummyHead;
while (pre->next != nullptr) {
ListNode *cur = pre->next;
/* 当前节点的值等于其下一节点的值,右移当前节点到其下一节点 */
while (cur->next != nullptr && cur->val == cur->next->val) {
cur = cur->next;
}
/* 当前节点不是 pre 节点的下一节点,则将 pre 节点的下一节点指向当前
节点的下一节点,相当于删除当前所有含跟当前节点值相等的节点 */
if (cur != pre->next) {
pre->next = cur->next;
/* 否则,pre 节点右移 */
} else {
pre = pre->next;
}
}
/* 释放虚拟头节点空间,防止内存泄漏 */
ListNode* retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
递归
当然这道题也可以通过递归的方法去做,递归终止条件:链表为空或者只有一个节点。循环判断当前节点的值是否等于其下一节点的值,如果等于,则将当前节点右移至其下一节点,然后再递归删除当前节点的下一节点后面子链表中的所有重复数字的节点;否则就递归当前节点的下一节点,挂接在当前节点后面。
Show me the Code
// c++
ListNode* deleteDuplicates(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}
if (head->val == head->next->val) {
while (head->next != nullptr && head->val == head->next->val) {
head = head->next;
}
return deleteDuplicates(head->next);
} else {
head->next = deleteDuplicates(head->next);
return head;
}
}