[In-place Reversal of a LinkedList] K个一组反转链表

leetcode 25,Reverse Nodes in k-Group,难度 hard

0. 题干

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

示例:
给你这个链表:1->2->3->4->5
当 k = 2 时,应当返回: 2->1->4->3->5
当 k = 3 时,应当返回: 3->2->1->4->5

说明:
你的算法只能使用常数的额外空间。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

1. 代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* dummy=new ListNode(0);
        dummy->next=head;//dummy->next存储链表头指针
        ListNode* node_first=head;// 存储链表首元地址,即结点1地址
        ListNode* pre=dummy;
        ListNode* cur=head;
        ListNode* tail=dummy;
        while(true){
            int count=0;
            tail=pre;
            while(tail!=NULL&&count<k){
                tail=tail->next; //退出循环后tail指向待反转链表的末尾节点
                count++;
            }
            if(tail==NULL)
                break;
            while(pre->next!=tail){ //pre->next==tail时退出循环
                cur=pre->next;//(1)
                pre->next=cur->next; //(1)  步骤(1):将cur从链表中切出来
                    
                cur->next=tail->next;//(2)
                tail->next=cur; //(2) 步骤(2):将cur添加到tail后
                
            }
            pre=node_first;
            tail=node_first;
            node_first=pre->next;//head指向新的待翻转的链表头
        }
        return dummy->next;
    }
};

2. 代码流程理解

在这里插入图片描述
假设原链表有7个结点,K的值为3,接下来一句一句跑下代码;由于链表有指向的概念(其实就是指针,以下为了区分,统一把链表的next指针表述为指向,各个结点的指针表述为存储地址)

初始化空链表dummy,dummy指向头指针head;
node_first存储首元结点的地址;
pre存储dummy的地址;
cur存储head的地址;
tail存储dummy的地址;

2.1 第一重外面while(true)循环

进入while循环,count初始化为0,tail和pre相等,此时都为dummy的地址;

tail当然不为NULL,0<3 ;
tail递增存储结点1的地址;
count递增为1;

count=1<3,然后再次进入while循环,
tail递增存储结点2的地址;
count递增为2;

count=2<3,然后再次进入while循环,
tail递增存储结点3 的地址;
count递增为3

不满足循环继续条件,所以while循环跳出;

if ( tail == NULL) 然后break这句,是while(true)的循环退出条件,总不能整个死循环吧…

在这里插入图片描述

while(pre->next!=tail){,前面已知,pre指向结点1,而此时tail存储结点3的
地址,所以再次进入while循环,

cur存储结点1的位置;
pre指向结点2;
结点1指向结点4
结点3指向结点1;

这波运行完之后,链表关系为:

2 -> 3 -> 1 ->4 ->5 ->6 ->7->NULL

由于pre指向结点2,不为结点3,所以while循环继续;
cur存储结点2的位置;
pre指向结点3;
结点2指向结点1;
结点3指向结点2;

这波运行完之后,链表关系为:
3->2->1->4->5->6->7->NULL

接下来pre存储链表头的位置,即结点1的位置,
tail存储结点头的位置,即结点1的位置,
node_first存储结点4的位置;

2.2 第二重while(true)循环

在这里插入图片描述

count 初始化为0;
tail和pre都存储结点1的位置;

while(tail!=NULL&&count<k){ 这个运行完之后,tail存储结点6的位置;

pre指向结点4,不指向结点6,所以进入 while(pre->next!=tail) 循环,
cur存储存储结点4的位置;
结点1指向结点5;
结点4指向结点结点7
结点6指向结点4

这波运行完之后,链表关系为:
3->2->1->5->6->4->7->NULL

此时pre指向结点5,而不是结点6,所以while循环继续,
cur存储结点5的位置;
pre指向结点6,即结点1指向结点6;
结点5指向结点结点4;
结点6指向结点5;

这波运行完之后,链表关系为:
3->2->1->6->5->4->7->NULL

跳出 while(pre->next!=tail) 循环,
然后pre存储结点4的位置;
tail存储结点4的位置;

node_first存储结点7的位置;

2.3 第三重while(true)循环

count初始化为0;
tail和pre都存储结点4的位置
while(tail!=NULL&&count<k){
由于k为3,运行一次,tail存储结点7的位置,
再运行一次,tail存储NULL,所以while循环跳出,

然后进入if ( tail == NULL) 跳出整个while循环;

在这里插入图片描述

3. 补充说明

这里特别补充,链表的头指针,头结点和首元结点;

在这里插入图片描述

头指针就是一个指针,没有数据,指向整个链表;如果没有头节点,那么头指针就是首元结点的地址。

一般来说,leetcode里面的链表是没有头结点的;

链表是带头结点的,头指针会始终指向头结点,不会改变。
如果链表不带头节点,头指针会始终指向队首元素,会改变。

这就是下面这句代码的意义。

return dummy->next;

更具体可参考:https://blog.csdn.net/u012531536/article/details/80170893

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值