链表-----

🔥个人主页: Milestone-里程碑

❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>

<<Git>><<MySQL>>

🌟心向往之行必能至

题目回顾

LeetCode 25. K 个一组翻转链表

给定一个链表的头节点 head 和一个正整数 k,每 k 个节点一组进行翻转,返回修改后的链表。如果节点总数不是 k 的整数倍,请将最后剩余的节点保持原有顺序。

示例

  • 输入:head = [1,2,3,4,5], k = 2 → 输出:[2,1,4,3,5]
  • 输入:head = [1,2,3,4,5], k = 3 → 输出:[3,2,1,4,5]

要求

  • 不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
  • 空间复杂度要求为 O(1)O(1) 。

🐛 遇到的"坑":一段看似正确的代码

在尝试解题时,我写下了如下代码(已简化):

cpp

编辑

复制代码
1class Solution {
2public:
3    ListNode* reverseKGroup(ListNode* head, int k) {
4        // ... 计算长度 n ...
5        
6        // ❌ 错误点 1:拷贝构造函数陷阱
7        ListNode dummy = ListNode(0, head); 
8        ListNode* p0 = &dummy;
9        ListNode* pre = nullptr;
10        ListNode* cur = head;
11
12        while (n >= k) {
13            n -= k;
14            // ❌ 错误点 2:pre 未重置
15            for (int i = 0; i < k; ++i) {
16                ListNode* nxt = cur->next;
17                cur->next = pre;
18                pre = cur;
19                cur = nxt;
20            }
21            // ... 连接逻辑 ...
22            p0 = p0->next; 
23        }
24        return dummy.next;
25    }
26};

这段代码在逻辑上似乎很通顺:先算长度,再分组翻转,最后拼接。但在实际提交时,却遇到了两个严重问题。

💥 错误一:被删除的拷贝构造函数

代码行:

cpp

编辑

复制代码
1ListNode dummy = ListNode(0, head);

在 LeetCode 的环境中,ListNode 结构体通常被定义为不可拷贝(Deleted Copy Constructor),以防止浅拷贝带来的指针混乱。因此,这行代码会直接报 Compile Error

✅ 修正方案

直接使用构造函数初始化,避免赋值拷贝:

cpp

编辑

复制代码
1ListNode dummy(0, head); // 正确:直接在栈上构造

💥 错误二:致命的指针逻辑漏洞

更隐蔽的错误在于变量 pre 的使用。

在第一次循环(第一组翻转)结束后,pre 指向了第一组的新头节点。

进入第二次循环(第二组翻转)时,pre 没有被重置为 nullptr

后果推演

当翻转第二组的第一个节点时,执行 cur->next = pre。此时 pre 还是第一组的尾节点(即原第一组的头)。

这导致:第二组的第一个节点指回了第一组,链表瞬间成环或顺序大乱!

✅ 修正方案

必须在每组翻转开始前,将 pre 重置为 nullptr


💡 核心解法:迭代 + 哨兵节点

修复上述错误后,我们得到一个标准的迭代解法 。其核心思想是:利用哨兵节点简化头节点处理,分组进行局部翻转,再重新连接

算法步骤详解

  1. 统计长度 :遍历链表获取总长度 n,用于判断剩余节点是否足够 k 个。
  2. 引入哨兵 :创建 dummy 节点指向 head,设 p0 指向 dummyp0 始终指向待翻转组的前一个节点)。
  3. 循环翻转
    • 检查剩余长度 n >= k
    • 重置指针pre = nullptr, cur = p0->next
    • 局部翻转 :执行 k 次标准链表翻转操作(cur->next = pre...)。
    • 重新连接
      • 记录当前组的旧头节点(翻转后变为尾):tail = p0->next
      • p0->next = pre:前驱连上新头。
      • tail->next = cur:旧头(现尾)连上后继节点。
    • 移动游标p0 = tail,准备处理下一组。
  4. 返回结果dummy.next

✅ 最终 AC 代码

cpp

编辑

复制代码
1class Solution {
2public:
3    ListNode* reverseKGroup(ListNode* head, int k) {
4        if (!head || k == 1) return head;
5
6        // 1. 计算链表总长度
7        ListNode* cur = head;
8        int n = 0;
9        while (cur) {
10            cur = cur->next;
11            ++n;
12        }
13
14        // 2. 初始化哨兵节点 (注意:直接构造,不要拷贝)
15        ListNode dummy(0, head);
16        ListNode* p0 = &dummy;
17        
18        // 重置 cur 指向真正的头节点
19        cur = head;
20        
21        while (n >= k) {
22            n -= k;
23            
24            // 【关键修复】每组翻转前,pre 必须重置为 nullptr
25            ListNode* pre = nullptr; 
26            
27            // 3. 局部翻转 k 个节点
28            for (int i = 0; i < k; ++i) {
29                ListNode* nxt = cur->next;
30                cur->next = pre;
31                pre = cur;
32                cur = nxt;
33            }
34
35            // 4. 重新连接链表
36            // p0 -> [旧头...旧尾] -> cur
37            // 翻转后:p0 -> [新头(pre)...新尾(旧头)] -> cur
38            
39            ListNode* tail = p0->next; // 记录当前的尾节点(即翻转前的头)
40            
41            p0->next = pre;      // 上一组尾 -> 当前组新头
42            tail->next = cur;    // 当前组尾 -> 下一组头
43            
44            p0 = tail;           // 更新 p0 为当前组的尾,准备处理下一组
45        }
46
47        return dummy.next;
48    }
49};

📊 复杂度分析

  • 时间复杂度 : O(N)O(N)
    • 第一次遍历计算长度耗时 O(N)O(N) 。
    • 后续每组翻转,每个节点仅被访问和反转一次,总耗时 O(N)O(N) 。
    • 整体为线性时间。
  • 空间复杂度 : O(1)O(1)
    • 仅使用了 dummy, p0, cur, pre, tail 等常数个指针变量。
    • 没有使用递归栈或额外数组,满足进阶要求。

🧠 总结与思考

这道题是链表操作的集大成者,它考察了以下几个关键点:

  1. C++ 基础细节:对象构造与拷贝控制(Rule of Three/Five),在刷题时也不能忽视语言特性。
  2. 指针状态管理 :在循环中,临时变量(如 pre)的状态是否需要在每轮重置?这是很多 Bug 的根源。
  3. 哨兵节点技巧dummy 节点能极大简化对头节点的特殊处理,使代码逻辑更统一。
  4. 分组处理模式while(n>=k) 配合内部 for 循环,是处理"每 K 个操作一次"类问题的通用模板。

避坑指南

  • 看到 ListNode 定义,下意识避免拷贝构造。
  • 写链表翻转循环时,时刻问自己:"这个指针在下一轮循环开始时,应该是多少?"
相关推荐
小O的算法实验室17 小时前
2026年ASOC,基于深度强化学习的无人机三维复杂环境分层自适应导航规划方法,深度解析+性能实测
算法·无人机·论文复现·智能算法·智能算法改进
‎ദ്ദിᵔ.˛.ᵔ₎17 小时前
LIST 的相关知识
数据结构·list
M--Y18 小时前
Redis常用数据类型
数据结构·数据库·redis
郭涤生18 小时前
STL vector 扩容机制与自定义内存分配器设计分析
c++·算法
༾冬瓜大侠༿18 小时前
vector
c语言·开发语言·数据结构·c++·算法
Ricky111zzz19 小时前
leetcode学python记录1
python·算法·leetcode·职场和发展
汀、人工智能19 小时前
[特殊字符] 第58课:两个正序数组的中位数
数据结构·算法·数据库架构··数据流·两个正序数组的中位数
liu****19 小时前
第16届省赛蓝桥杯大赛C/C++大学B组(京津冀)
开发语言·数据结构·c++·算法·蓝桥杯
汀、人工智能19 小时前
[特殊字符] 第79课:分割等和子集
数据结构·算法·数据库架构·位运算·哈希表·分割等和子集
汀、人工智能19 小时前
[特殊字符] 第74课:完全平方数
数据结构·算法·数据库架构·图论·bfs·完全平方数