本文最后更新于79 天前,其中的信息可能已经过时,如有错误请发送邮件到mocic1ovo@gmail.com
题目链接:25. K 个一组翻转链表
题目要求我们每 k 个节点为一组进行翻转,如果最后剩余的节点数量不足 k,则保持原有顺序不变。
这道题的难点并不在于“翻转链表”本身,而在于如何正确处理每一组之间的连接关系,以及如何在多次翻转过程中保证指针始终合法。
一、整体思路
可以将问题拆分成两个部分:
- 如何翻转一段指定区间的链表
- 如何按
k个节点一组,循环处理整条链表
二、区间翻转的设计
我们定义一个函数,用来翻转从 head 到 tail 的这一段链表,并返回翻转后的新头和新尾。
函数签名
pair<ListNode*,ListNode*> myRe(ListNode* head, ListNode* tail)
翻转核心思路
prev指向tail->next,作为翻转后的终止位置- 从
head开始逐个反转指针 - 当
prev == tail时,翻转结束
代码实现
pair<ListNode*,ListNode*> myRe(ListNode* head, ListNode* tail){
ListNode* prev = tail -> next;
ListNode* p = head;
while(prev != tail){
ListNode* ne = p -> next;
p -> next = prev;
prev = p;
p = ne;
}
return {tail, head};
}
翻转完成后:
- 原来的
tail变成新的head - 原来的
head变成新的tail
三、主函数整体框架
引入虚拟头节点
ListNode* hair = new ListNode(0);
hair -> next = head;
ListNode* pre = hair;
引入虚拟头节点 hair,可以统一处理头节点翻转的情况,避免额外的边界判断。
四、按 k 个节点一组遍历
while(head){
ListNode* tail = pre;
for(int i = 0;i < k; i++){
tail = tail -> next;
if(!tail){
return hair -> next;
}
}
这一步用于判断:
- 当前剩余链表长度是否不少于
k - 如果不足
k,直接返回结果
五、翻转并重新连接链表
ListNode* nex = tail -> next;
tie(head, tail) = myRe(head,tail);
pre -> next = head;
tail -> next = nex;
操作顺序非常关键:
- 记录
tail->next,防止链表断裂 - 翻转当前区间
- 将翻转后的新头接到
pre后面 - 将翻转后的新尾接回剩余链表
六、移动指针,进入下一轮
pre = tail;
head = tail -> next;
此时:
pre移动到当前翻转后的尾节点head指向下一组的起始位置
七、完整代码
class Solution {
public:
pair<ListNode*,ListNode*> myRe(ListNode* head,ListNode* tail){
ListNode* prev = tail -> next;
ListNode* p = head;
while(prev != tail){
ListNode* ne = p -> next;
p -> next = prev;
prev = p;
p = ne;
}
return {tail,head};
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* hair = new ListNode(0);
hair -> next = head;
ListNode* pre = hair;
while(head){
ListNode* tail = pre;
for(int i = 0;i < k; i++){
tail = tail -> next;
if(!tail){
return hair -> next;
}
}
ListNode* nex = tail -> next;
tie(head, tail) = myRe(head,tail);
pre -> next = head;
tail -> next = nex;
pre = tail;
head = tail -> next;
}
return hair -> next;
}
};
干货满满