Leetcode 力扣hot100 ——K 个一组翻转链表C++题解

K 个一组翻转链表C++题解

1.题目描述

给你链表的头节点 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]

2.参数条件

链表中的节点数目为 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000

3.题目分析

  • 首先,当k =1时,链表无需反转直接返回即可。所以我们着重分析 k >=2时的情况。

  • 当k >= 2时, 我们将全部链表每k个划分成一段,而链表的总长度n是要大于等于k的,所以划分的段数是要大于等于1的。

  • 现在我们来讨论一下具体的一段该怎么反转。中间部分是很好分析的:
    假设:中间部分的链表关系是:
    …A——>B———>C——>
    那么我们反转链表就行了,也就是把他们的关系变为:
    …A<——B ( 此处断开)C——>
    代码就是

//此处代码只是方便理解
LeftNode = A;    //A location
LeftNodeNext = LeftNode->next;   // B location
LeftNode3 = LeftNodeNext->next;   //C location
LeftNodeNext ->next = LeftNode;   //  B——>A
LeftNode = LeftNodeNext;  //更新LeftNode 到B
LeftNodeNext = LeftNode3 ;
LeftNode3  = LeftNode3 ->next;

这里我用了三个链表指针,主要是因为,在将A与B关系反转后,如果只用两个链表指针我们就会丢失掉C的位置,所以我们需要第三个链表指针来记录C的位置,以便可以在下一次将进行B<——C的操作,进而可以把整段的链表进行反转。

  • 接下来我们再来分析一下每一段开头的部分
    例如,K=3,链表关系如下:
    A——>B——>C——>D——>E——>F——>
    从D开始我进行的标粗,因为他属于下一段,为什么要写出下一段的链表关系呢 ? 因为这里我们需要使用到下一段的链表。
    我们知道反转后的关系应该是:
    C——>B——>A——>F——>E——>D
    是的反转后A的下一个链表应该是F!!,他是下一段链表的结尾。
    所以这就要求我们在反转的开头就要知道F的链表指针!(也只有开头部分会用到)
    我们的做法是:再添加一个右链表指针,让他指向F。换句话说,让RightNode领先LeftNode 的步长是(2 *k -1)。所以,在程序的最开始我们先让RightNode走2k-1步,再开始进行后续的反转,也就有:
while( RightNode != nullptr && steps < 2 * k -1){
            RightNode = RightNode->next;
            steps++;
            if( steps == k -1)
                HeadNoed = RightNode;   //这个主要用于记录返回的头指针
        }

于是,在每段的开头部分我们可以让LeftNode指向RightNode 的位置

LeftNode->next = RightNodeHead;
  • 那么最后我们只需讨论一下每一段的尾部的部分
    我们仍然假设K=3,反转前的关系是:B——>C——>D
    C是尾部,D是下一段的开头部分,我们期待的反转后的结果是:C——>B …( 断开 ) D
    B与C的反转和中间部分一样,重要的是我们如何跳转到下一段的开头,显然和中间部分一样的我们需要记录下D的位置,然后将LeftNode更新到D处,这样就进入了下一段的开头。
//此处代码只是方便理解
LeftNode = B;    //B location
LeftNodeNext = LeftNode->next;   // C location
LeftNode3 = LeftNodeNext->next;   //D location
LeftNodeNext ->next = LeftNode;   //  B——>A
LeftNode = LeftNodeNext;  //更新LeftNode到C
LeftNodeNext = LeftNode3;  
LeftNode3  = LeftNode3 ->next; 
//比中间部分多的代码:
LeftNode  = LeftNodeNext;  /更新LeftNode到D
LeftNodeNext = LeftNode3;  
LeftNode3  = LeftNode3 ->next; 
  • 还有一点需要注意的是:当我们对LeftNode向右移动进行反转操作时,需保证LeftNode进入下一个链表段开头时,RightNode也要进入对应下一个链表段的结尾。即RightNode和LeftNode在完成一个链表段的反转后,他们移动的步长要一致,这样才能保证在开头部分,LeftNode指向RightNode。

4.何时结束?

何时结束是一个非常关键的问题,但我们只需考虑处理最后一个链表段时所需的反转处理。

  • 如果n是k的整数倍,那么我们需要在RightNode变为空时,记录反转处理即将完成(使用non_over 记录)。并在正常完成下一段的反转后结束所有操作,然后返回处理完成后的链表头即可。
  • 如果n不是k的整数倍,那么会有一个多余段(长度小于k)不需要反转操作。那么RightNode也会在移动一定步长(这个步长<k)后变为空,那么我们也记录下处理即将完成(non_over)。但是不同的是,在进行下一段链表反转操作(也即最后一段链表反转)的开始部分,LeftNode不再指向RightNode,而是指向多余段的链表头。所以我们需要一个链表指针RightNodeHead,记录每次RightNode所在段的链表头。

5.代码实现

题目分析结束,那么我们就开始解题吧,在解题的过程中,会发现需要对LeftNode3 的访问越界问题进行考虑,当然着只会在最后一次反转中出现。
这里主要是C++的实现:

/**
 * 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:
    ListNode* reverseKGroup(ListNode* head, int k) {
        if( k == 1)
            return head;
        ListNode* RightNode = head;
        ListNode* LeftNode = head;
        ListNode* HeadNoed = nullptr;
        ListNode* LeftNext = nullptr;
        ListNode* LeftNode3 =  nullptr;
        ListNode* RightNodeHead = nullptr;
        int steps = 0;
        bool non_over = true;

        while( RightNode != nullptr && steps < 2 * k -1){
            RightNode = RightNode->next;
            steps++;
            if( steps == k -1)
                HeadNoed = RightNode;
        }
         if( !(steps == 2 * k -1 && RightNode != nullptr) )
            RightNodeHead = HeadNoed->next;      //长度小于2段,结束反转预警,需要记录RightNode所在段的链表头
        LeftNext = head->next; 
        LeftNode3 = LeftNext->next;  
        while( non_over){
            if(RightNode == nullptr){
                non_over = false;             //RightNode 说明是最后一次反转
                LeftNode->next = RightNodeHead;
            }
            else
                LeftNode->next = RightNode;  
            for( int j = 0; j < k -1 ; ){
                j++; 
                LeftNext->next = LeftNode;   
                LeftNode = LeftNext;  
                LeftNext = LeftNode3; 
                if( non_over || j != k-1)
                    LeftNode3 = LeftNext->next;   
            }  
            if( non_over ){
                LeftNode = LeftNext; 
                LeftNext = LeftNode3;
                LeftNode3 = LeftNext->next;
            } 
            steps = 0;
            if( non_over)
                RightNodeHead = RightNode->next;
            while( RightNode != nullptr && steps < k){
                RightNode = RightNode->next;
                steps++;    
            }
        }
 
        return HeadNoed;
    }
};
  • 上述代码可以提交成功,同样解题思路也可以使用JAVA/Python/Go进行编写
    在这里插入图片描述
    时间复杂度
    在这里插入图片描述
    空间复杂度:
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值