力扣25:K 个一组翻转链表

1.题目描述

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
来源:力扣(LeetCode)
链接:K个一组翻转链表.

2.题目分析

首先,这是一个翻转链表的问题,翻转普通链表的问题可以通过 翻转链表以及反转链表II进行训练.
根据题目描述,我们可以了解,他需要每K个节点进行一次反转,而且末尾节点若不够K个,则保持原有顺序。
额外条件限制了我们不能进行读值放入数组进行操作。所以这就需要我们进行在原链表进行反转。

3.题目讲解

若K=2,则需要反转成21435.
在这里插入图片描述
在这里插入图片描述
这里我们借助反转链表II的思想,将需要反转的地方进行头插。这里我们需要一个哨兵节点进行第一个节点的头插。
在这里插入图片描述
然后,每次走K个节点,进行反转。这里我们用cur这个指针进行链表的遍历。还需要一个first指针记录这个头插链表的头。刚开始值为1的节点就是头插链表的头。然后需要一个pre指针进行头插操作。
在这里插入图片描述
将pre的值取下来进行头插,则实现了一次反转。
在这里插入图片描述
这里头插需要将pre先从链表中取下来。具体代码为

first->next=pre->next;
pre->next=first;
nhead->next=pre;

头插完后,将pre指针向后挪动,在这个问题中也就是将first后面的值递给pre。
在这里插入图片描述
因为k=2,所以只需要反转两个,那么循环的终止条件就是当pre等于cur时,反转的循环结束。这时候就进行下K个链表反转。
退出反转逻辑后,cur指针继续向后走k个节点。

while(cur!=nullptr&&k){
cur=cur->next;
k--;
}
if(cur==nullptr&&nums>0)//表示后面节点不足k个
break;

这里需要注意的是,当节点数不足k个时,则不用进行反转。所以我们在循环终止条件需要加入cur!=nullptr。
每当cur走之前,需要将first更新,使first指针指向每K个链表的开头。pre则指向first的后面(需要将后面的值进行头插)。
当链表走完后,反转完成。

这里我们考虑到了第一中情况,就是k=2时的简单操作,当k等于3或者说更大时,我们需要引进last指针和kcl指针进行操作。
还是刚才的问题,将12345进行反转,那么反转后应该为32145。
在这里插入图片描述
在这里插入图片描述
这里的思路与上面类似,唯一不同的就是反转时的头插。
刚开始,cur走到了4,那么每次进行循环时第一步还是和上面一样,先将2头插。
在这里插入图片描述
还是刚才的逻辑,但是,当进行下一次头插(将3插入到2之前),我们需要对pre以及first进行更新,让first指向2,让pre指向3。因为每次头插,first指针都会在kcl指针的后面,所以更新时直接让first=kcl->next。last指针则是保存头插节点下一个节点,这样可以让pre指针在进行头插完后可以找到需要下次头插的节点。
在这里插入图片描述

当pre等于cur时表示反转结束,这里反转的代码则会变为

while(pre!=nullptr&&pre!=cur){
last->next=pre->next;
pre->next=first;
kcl->next=pre;
pre=last->next;
first=kcl->next;
}

当将K个反转完后,需要将kcl和first同时进行更新,

kcl=last;//表示上一组链表的结尾
first=cur;//表示下一组链表的开头

在这里插入图片描述

当走到末尾,那么反转结束。
最后返回nhead->next。

4.实现代码

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if(head==nullptr||head->next==nullptr)
        return head;
        ListNode*cur=head;
        ListNode*pre=nullptr;//用来进行头插
        ListNode*last=nullptr;//保存第K个节点
        ListNode*first=head;//每次头插后的下一个节点
        ListNode*nhead=new ListNode();//哨兵节点
        nhead->next=head;
        ListNode*kcl=nhead;//表示反转前的前一组的最后一个节点
        int num=k;
        while(cur!=nullptr){
            first=cur; //保存每组反转的第一个节点
            while(cur!=nullptr&&num){ //找到每组的结尾
               cur=cur->next;
               num--;
            }
            if(cur==nullptr&&num>0)//如果走到整个链表的结尾且后面的节点总数小于K时直接退出
            break;
            pre=first->next;
            last=first;
            while(pre!=nullptr&&pre!=cur){//每次插入时,将后一个节点拿出来进行头插
              last->next=pre->next; //因为头插后原来的第一个节点就成了最后一个节点
              pre->next=first; //所以每次需要进行保存最后一个节点
              kcl->next=pre; //然后一直进行头插,更新first和pre
              pre=last->next;
              first=kcl->next;
            }
            kcl=last;//反转完后将前一组最后一个节点进行更新
            num=k;//让cur继续往后走
        }
        return nhead->next;
    }
};
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值