C++ 链表技巧
·
删除满足某条件的节点
直接遍历比较麻烦,因为头结点和其他节点删除逻辑不同。简单方法:递归、虚拟头结点。
直接遍历代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
ListNode* removeElements(ListNode* head, int val) {
ListNode* pre = head;
ListNode* cur = head;
ListNode* new_head = head;
while (cur) {
if (cur->val == val) {
if (cur == new_head) {
new_head = cur->next;
} else
pre->next = cur->next;
ListNode* tem = cur;
cur = cur->next;
delete tem;
} else {
pre = cur;
cur = cur->next;
}
}
return new_head;
}
递归
时间复杂度:O(n),空间复杂度:O(n)。
ListNode* removeElements(ListNode* head, int val)
{
if (!head)
return head;
head->next = removeElements(head->next, val);
return head->val == val ? head->next : head;
}
虚拟头结点
在头结点前添加虚拟的头结点,统一头结点和其他节点的删除逻辑。时间复杂度:O(n),空间复杂度:O(1)。可能涉及头结点的增删的情况,都可以使用虚拟头结点简化逻辑。
ListNode* removeElements(ListNode* head, int val)
{
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* temp = dummyHead;
while (temp->next) {
if (temp->next->val == val) {
temp->next = temp->next->next;
} else {
temp = temp->next;
}
}
return dummyHead->next;
}
快慢指针:处理环形链表
快慢指针可用于检测链表中的环,并求出入环的第一个节点。原理:快指针每次移动两步,而慢指针每次只移动一步,当链表中存在环时,快指针与慢指针必定相遇。
设第一次相遇,快指针走了2k步,慢指针走了k步,环外长度a, 环长b
快指针多走了n圈(nb) --> k = nb , 慢指针走了nb步。
若慢指针再走a步,则一共走了a+nb步,刚好到达环入口所以在慢指针和快指针相遇时,再使用一个指针ptr,初始它指向链表头部。随后,它和慢指针每次往后走一步,最后它们会在环入口相遇。
142. 环形链表 II - 力扣(LeetCode),题解代码写得很清楚。
此处,提供另一种解题思路:基于链表从头结点到尾结点依次使用new创建的假设,和堆地址从低到高生长的背景知识,令pre指针为前一节点,cur指针为当前节点。cur-pre>0,说明cur指向的节点在pre后面创建;cur-pre<=0,说明从链表尾部再次入环,且cur为入环的第一个节点。
ListNode* detectCycle(ListNode* head)
{
// suppose that all nodes are created in sequence
// 堆地址是从低到高生长
ListNode* pre = head;
if (!head || !head->next)
return nullptr;
ListNode* cur = head->next;
while (cur) {
if (cur - pre <= 0)
return cur;
pre = cur;
cur = cur->next;
}
return nullptr;
}
更多推荐


所有评论(0)