给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]
提示:
- 链表中的节点数目为
n
1 <= k <= n <= 5000
0 <= Node.val <= 1000
进阶:你可以设计一个只用 O(1)
额外内存空间的算法解决此问题吗?
解析:
将链表分成若干组,每组包含k个节点,然后对每组内的节点进行反转,同时保持组与组之间的相对顺序不变。解决办法的思路是首先创建一个虚拟头节点来简化头节点操作,然后遍历链表,每次检查是否有至少k个节点,如果有,则使用一个辅助函数来反转这k个节点,接着将反转后的节点组重新连接到原链表中,最后返回新链表的头节点。这个过程需要在每次反转后更新指针,以确保链表的连续性和正确性。
代码:
/**
* 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:
// 定义一个pair,用于存储反转链表的头尾节点
pair<ListNode*, ListNode*> myReverse(ListNode* head, ListNode* tail){
ListNode* prev = tail->next; // 初始化prev为tail的下一个节点,即反转后的第一个节点
ListNode* p = head; // 初始化p为当前处理的节点,从头节点开始
while (prev != tail){ // 遍历链表直到prev等于tail,即到达需要反转的链表的末尾
ListNode* nex = p->next; // 保存p的下一个节点
p->next = prev; // 反转p的next指针,指向prev
prev = p; // 移动prev到p的位置
p = nex; // 移动p到下一个节点
}
return {tail,head}; // 返回反转后的链表头尾节点
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* hair = new ListNode(0); // 创建一个虚拟头节点,方便处理原链表头
hair->next = head; // 虚拟头节点的next指向原链表头
ListNode* pre = hair; // 初始化pre为虚拟头节点,用于追踪反转组的前一个节点
while(head){ // 遍历链表直到到达末尾
ListNode* tail = pre; // 初始化tail为pre,即当前组的起始节点
for(int i=0;i<k;++i){ // 检查是否有足够的节点进行反转
tail = tail->next; // 移动tail指针,寻找当前组的尾节点
if(!tail){ // 如果tail为空,说明没有足够的节点进行反转
return hair->next; // 返回原链表头
}
}
ListNode* nex = tail->next; // 保存tail的下一个节点,即下一组的头节点
tie(head,tail) = myReverse(head, tail); // 调用myReverse反转当前组
pre->next = head; // 将反转后的头节点连接到pre的后面
tail->next = nex; // 将反转后的尾节点连接到下一组的头节点
pre = tail; // 更新pre为当前组的尾节点,即下一组的起始节点
head = tail->next; // 更新head为下一组的头节点
}
return hair->next; // 返回新链表的头节点
}
};