【Hot 100 刷题计划】 LeetCode 25. K 个一组翻转链表 | C++ 链表题解
·
LeetCode 25. K 个一组翻转链表 | C++ 迭代法最优解
📌 题目描述
题目级别:困难 (Hard)
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k是一个正整数,它的值小于或等于链表的长度。- 如果节点总数不是
k的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶要求:你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换(即空间复杂度要求为 O ( 1 ) O(1) O(1))。
💡 解题思路:分组模拟 + 迭代反转
这道题是“反转链表”的终极进阶版。核心思想可以拆解为三个步骤:先数数,再分组反转,最后重连首尾。
1. 统计节点总数
首先遍历一遍链表,统计出总节点数 n。这样我们就知道一共可以凑出多少个完整的 k 个一组,当 n < k 时,说明剩下的节点不够一组了,直接停止反转。
2. 引入虚拟头节点 (Dummy Node)
由于链表的头节点在反转后会发生改变,为了避免复杂的边界讨论,我们引入一个虚拟头节点 dummy,让 dummy->next = head。
同时定义一个指针 p0,它永远指向当前准备反转的这一组节点的前一个节点。初始时 p0 = dummy。
3. 组内反转与首尾重连(核心逻辑)
在每一轮反转中(条件是 n >= k),我们需要做两件事:
- 组内反转:使用标准的链表反转逻辑(
pre,cur,ne),将这k个节点反转。 - 首尾重连:反转结束后,原本这组的第一个节点变成了最后一个节点,原本的最后一个节点变成了第一个节点。我们需要把它们与原链表重新缝合!
重连代码剖析:
ListNode* ne = p0->next; // p0->next 是这组原来的头,反转后它变成了尾部,我们用 ne 暂时存下来
p0->next->next = cur; // 原来的头(现在的尾)需要指向下一组的开头(即反转结束后 cur 所在的位置)
p0->next = pre; // p0(当前组的前驱节点)需要指向这组反转后的新头节点(即 pre)
p0 = ne; // p0 移动到当前组的尾部,作为下一组的前驱节点,继续下一轮!
💻 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) {}
* };
*/
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
// 1. 统计链表长度
int n = 0;
ListNode* cur = head;
while (cur) {
n++;
cur = cur->next;
}
// 2. 初始化虚拟头节点和前驱指针 p0
ListNode* dummy = new ListNode(-1, head);
ListNode* p0 = dummy;
ListNode* pre = nullptr;
cur = p0->next; // cur 重新回到链表真实的头节点
// 3. 只要剩下的节点够 k 个,就进行分组反转
while (n >= k) {
n -= k; // 扣除本轮的 k 个节点
// --- 组内反转核心逻辑 ---
for (int i = 0; i < k; i++) {
ListNode* ne = cur->next;
cur->next = pre;
pre = cur;
cur = ne;
}
// --- 首尾重连核心逻辑 ---
ListNode* ne = p0->next; // 记录当前组最初的头节点(反转后变成当前组的尾节点)
p0->next->next = cur; // 将当前组的新尾节点连上后续未反转的链表
p0->next = pre; // 将当前组的前驱节点连上当前组的新头节点
// 为下一组反转做准备
p0 = ne;
}
return dummy->next;
}
};
更多推荐



所有评论(0)