系网站建设工作总结个人网站做百度竞价
2026/1/28 19:52:04 网站建设 项目流程
系网站建设工作总结,个人网站做百度竞价,代理公司注册费用,代运营公司网站链表#xff08;Linked List#xff09;一直是让人爱恨交加的数据结构。爱它是因为结构简单#xff0c;恨它是因为指针操作稍有不慎就会导致断链或空指针异常。今天通过两个经典场景——删除链表的倒数第 N 个结点和两两交换链表中的节点#xff0c;来探讨链表操作中的两个…链表Linked List一直是让人爱恨交加的数据结构。爱它是因为结构简单恨它是因为指针操作稍有不慎就会导致断链或空指针异常。今天通过两个经典场景——删除链表的倒数第 N 个结点和两两交换链表中的节点来探讨链表操作中的两个通用范式固定间距的双指针以及基于多指针的局部拓扑重构。场景一删除链表的倒数第 N 个结点这道题是“快慢指针”思想的经典应用。通常我们要删除倒数第 N 个节点朴素的做法是先遍历一遍求长度 L再遍历一遍找到第 L-N 个节点。但这需要两趟扫描。如何通过一趟扫描完成任务核心在于**“构建固定窗口”**。核心思路我们可以想象在链表上有一个长度为 n 的“尺子”或者“窗口”让 right 指针先走 n 步。此时 right 和 left 之间隔了 n 个节点。然后 left 和 right 同时向后移动直到 right 抵达链表末尾。此时left 指针所在的位置正好是倒数第 N 个节点的前驱节点。C 代码实现C代码实现class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { // 技巧使用栈上分配的 Dummy Node避免内存管理麻烦且处理了头删边缘情况 ListNode dummy(0, head); ListNode* right dummy; ListNode* left dummy; // 1. 构建间距先让 right 走 n 步 for(int i 0; i n; i) { right right-next; } // 2. 整体平移直到 right 指向最后一个节点 // 注意这里我们让 right 停在最后一个节点而不是 null // 这样 left 正好停在待删除节点的前一个节点 while (right-next) { left left-next; right right-next; } // 3. 删除操作 ListNode* toDelete left-next; left-next left-next-next; // 在实际工程中建议 delete toDelete刷题场景可忽略 return dummy.next; } };为什么一定要用 Dummy Node在链表操作中哨兵节点Dummy Node非常实用。 如果不使用 dummy当我们要删除的是**头节点倒数第 L 个**时left 指针应该停在头节点的前面但实际不存在这个节点就需要写额外的 if 逻辑判断。 使用了 dummy 后dummy-next 指向 head即便是删除头节点也变成了“删除 dummy 的下一个节点”逻辑瞬间统一无需处理边缘情况。复杂度分析时间复杂度O(L)。其中 L 是链表长度。我们只对链表进行了一次遍历。空间复杂度O(1)。只使用了 dummy, left, right 等常数个额外空间。场景二两两交换链表中的节点如果说上一题是对遍历技巧的运用这道题考验的就是微观的**“指针手术”**。我们需要在不改变节点值Val的情况下通过修改 next 指针来改变链表的拓扑结构。核心思路四指针定点操作链表交换最怕的是断链。当我们交换两个节点 A 和 B 时不仅要处理 A 和 B 之间的连接还要处理 A 的前驱节点和 B 的后继节点。我们可以定义四个指针清晰地锁定了整个操作区域cur0: 前驱节点 (Prev)cur1: 待交换节点 A (First)cur2: 待交换节点 B (Second)cur3: 后继节点 (Next)C 代码实现C代码实现class Solution { public: ListNode* swapPairs(ListNode* head) { // 同样使用 Dummy Node 处理头节点交换的情况 ListNode dummy(0, head); ListNode* cur0 dummy; // 前驱 ListNode* cur1 cur0-next; // 当前第一个 // 终止条件必须要有两个节点才能交换 (cur1 cur1-next) while (cur1 cur1-next) { // 定位 cur2 和 cur3 ListNode* cur2 cur1-next; ListNode* cur3 cur2-next; // --- 核心交换逻辑 (三步走) --- cur0-next cur2; // 1. 前驱指向第二个 cur2-next cur1; // 2. 第二个指向第一个 cur1-next cur3; // 3. 第一个指向后续 // --- 准备下一轮迭代 --- // 此时链表顺序已变为 0-2-1-3 // 下一轮的前驱 cur0 应该是当前的 cur1 (因为它被换到了后面) cur0 cur1; // 下一轮的第一个节点应该是 cur3 cur1 cur3; } return dummy.next; } };变量命名的逻辑虽然在工程中我们习惯用 prev, curr, next 命名但在涉及超过 3 个节点的复杂变换中使用 cur0, cur1, cur2, cur3 这种序列化命名反而更清晰。它直观地展示了节点在原链表中的物理顺序让**“谁指向谁”**的逻辑变得一目了然。在循环迭代时cur0 cur1和cur1 cur3这两行代码非常关键它实现了“滑动窗口”向后平移两位的效果。复杂度分析时间复杂度O(L)。我们需要遍历链表中的每一个节点一次。空间复杂度O(1)。虽然定义了4个指针变量但这属于常数级别的额外空间不随链表长度增加。总结这两个场景虽然解法不同但背后的核心思想是一致的哨兵节点Dummy Node是处理链表边界的最佳实践。无论是删除倒数节点还是交换头节点它都能让代码逻辑高度统一。多指针辅助。不要吝啬定义指针变量。在复杂的链表操作中明确定义出涉及到的所有节点前驱、当前、目标、后继比在代码里写一堆 head-next-next-next 要清晰得多也更不容易出错。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询