LEETCODE(力扣) 25. K 个一组翻转链表
给你链表的头节点 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) 额外内存空间的算法解决此问题吗?
自解

因为每次只对部分节点进行处理,因此需要记录处理段前、后的节点,而头节点因无更前面的节点,需要单独写处理逻辑,因此可以用一个dummy节点插入头节点前面,减少代码量。
因为要与dummy节点的处理逻辑保持一致,我们每次reverse传入的参数要传入处理段的前一个节点。
如下,每次处理传入节点后的k个节点,处理完毕后将处理段首节点连接到已处理段尾节点上,本次处理段的尾节点连在未处理段的首结点上,并返回本次处理段的尾节点
主函数while循环到全部处理完毕或剩余节点不足k时(reverse都返回NULL)退出。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode *reverse(struct ListNode *head,int k)
{
/*last、next0用于反转链表时的中间变量,next1记录处理段的后一个节点,head1记录处理段的前一个节点*/
struct ListNode *last=head,*next0=NULL,*next1=NULL,*head1=head;
int i=k;
head=head->next;//从处理段开始反转
next1=head;
while(next1&&i)//遍历到处理段的后一个节点,同时检查节点数量是否足够
{
i--;
next1=next1->next;
}
if(i)return NULL;//节点数量不够时返回
while(head&&k)//开始对节点进行反转,反转k个
{
next0=head->next;
head->next=last;
last=head;
head=next0;
k--;
}
head1->next=last;//last是该次反转后的处理段首结点,连接到已处理段的尾节点上
while(last->next!=head1)last=last->next;//将last遍历到尾节点上
last->next=next0;//该次反转后的处理段尾节点连接到未处理段上
return last;//该次处理段的尾节点
}
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
struct ListNode dummy,*temp=NULL,*last=NULL,*head1=NULL;
dummy.next=head;
head=&dummy;
while(head->next&&(head=reverse(head,k)));
return dummy.next;
}
力扣解
自解的反转处理与该解法相同,但该解法代码更加简洁,for循环的判断也值得深究
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
// 统计节点个数
int n = 0;
for (struct ListNode* cur = head; cur; cur = cur->next) {
n++;
}
struct ListNode dummy = {0, head};
struct ListNode* p0 = &dummy;
struct ListNode* pre = NULL;
struct ListNode* cur = head;
// k 个一组处理
for (; n >= k; n -= k) {
for (int i = 0; i < k; i++) { // 同 92 题
struct ListNode* nxt = cur->next;
cur->next = pre; // 每次循环只修改一个 next,方便大家理解
pre = cur;
cur = nxt;
}
// 见视频
struct ListNode* nxt = p0->next;//--p0->next此时指向该次处理段的尾节点
p0->next->next = cur;//--p0->next->next是未处理段的首结点
p0->next = pre;//--pre是该次处理段的尾节点
p0 = nxt;//--p0指向该次处理段的尾节点
//--这段处理就是为了使cur指向已处理段的最后一个节点,原因就是我在自解说的,减少代码处理量,不必单独对首结点进行逻辑处理(ps:--注释仅为个人理解,与灵佬无关)
}
return dummy.next;
}
作者:灵茶山艾府
链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/solutions/1992228/you-xie-cuo-liao-yi-ge-shi-pin-jiang-tou-plfs/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
仿解
个人认为灵佬的代码更简洁也更优美,进行了仿写练习
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseKGroup(struct ListNode* head, int k) {
int i=0;
struct ListNode dummy={0,head};
struct ListNode *p=&dummy;
struct ListNode *cur=head,*nxt=head,*last=NULL;
while(nxt)
{
nxt=nxt->next;
i++;
}
for(;i>=k;i-=k)
{
for(int j=0;j<k;j++)
{
nxt=cur->next;
cur->next=last;
last=cur;
cur=nxt;
}
nxt=p->next;//nxt存放处理段尾节点
p->next=last;//连接已处理段尾节点与处理段首结点
nxt->next=cur;//处理段尾节点连接未处理段首结点
p=nxt;
}
return dummy.next;
}

浙公网安备 33010602011771号