由于链表原地算法的循环条件的的设置与思考容易消耗时间,而如果设置错误,就需要模拟走一遍,很浪费时间,还不一定对。所以我直接对链表问题结束循环的条件判断进行了全面的总结并列出表格
目标 / 场景 | 必要条件 | 循环条件写法 | 指针初始化 | 示例说明 |
---|---|---|---|---|
遍历整条链表 | 当前节点不为空 | while (cur) | ListNode* cur = head; | 常用于打印、计数等 |
访问 cur->next | cur 和 cur->next 都不为空 | while (cur && cur->next) | ListNode* cur = head; | 需要访问下一个节点 |
访问 cur->next->next | 三个节点均不为空 | while (cur && cur->next && cur->next->next) | ListNode* cur = head; | 如判断三节点趋势 |
两两交换节点 | 当前和下一个节点存在 | while (cur && cur->next) | 可设置虚拟头:ListNode dummy(0); dummy.next = head; ListNode* cur = &dummy; | 交换 cur->next 和 cur->next->next |
判断链表是否有环(快慢指针) | 快指针不能空 | while (fast && fast->next) | ListNode* slow = head; ListNode* fast = head; | Floyd 判圈算法 |
寻找环的入口 | 慢指针从头、快指针从相遇点同时走 | while (p1 != p2) | ListNode* p1 = head; ListNode* p2 = meetingPoint; | 用于找环入口 |
删除指定节点(非头) | 判断前驱的 next 是否目标 | while (cur && cur->next) | ListNode* cur = head; 或带 dummy | 判断 cur->next->val == val |
删除所有值为 val 的节点 | 全遍历判断 | while (prev->next) | ListNode dummy(0); dummy.next = head; ListNode* prev = &dummy; | prev->next = prev->next->next |
找倒数第 k 个节点(快慢指针) | 快指针先走 k 步 | while (fast) 或 while (fast->next) | ListNode* slow = head; ListNode* fast = head; ,快指针先走 k 步 | 常用于找删除位置或返回节点 |
链表反转 | 遍历所有节点并重建指向 | while (cur) | ListNode* prev = nullptr; ListNode* cur = head; | 3 指针反转写法 |
合并两个有序链表 | 两链表均未空 | while (l1 && l2) | ListNode dummy(0); ListNode* tail = &dummy; | 比较 l1 和 l2 |
K 个一组翻转链表 | 当前组有 k 个节点 | for (int i = 0; i < k && p; ++i) + 判空 | ListNode* groupPrev = dummy ,辅助函数查 k 个节点 | LC25 题经典思路 |
奇偶链表分组 | even 和 even->next 不为空 | while (even && even->next) | ListNode* odd = head; ListNode* even = head->next; ListNode* evenHead = even; | odd 指向奇数位置节点,even 走偶数链 |
附加建议
-
dummy 节点(辅助头节点)几乎是链表处理万能技巧,可以统一头节点的处理逻辑,避免特判。
ListNode* dummy = new ListNode(0);
dummy->next = head;
ListNode* cur = dummy;