【力扣刷题】【1-50】25. K 个一组翻转链表

25. K 个一组翻转链表

1.模拟交换

  • 本题比较经典,建议多练习。如果是初学,建议在完成24. 两两交换链表中的节点,可以理解哑巴节点的作用,能理解链表操作的特性再练习。

  • 本题和两两反转链表的原理是相同的,但是需要做到任意K个反转,并且在不足K个的时候以正常顺序返回不足K个的这一部分。

  • 但是本题实现起来却有一定难度,这里提及解决链表问题的一个重要心得:

    大量设变量,能有效降低分析复杂度

    因为链表的结构,导致在解决问题时,会涉及大量的->的操作,往往在操作之后,原有的顺序结构发生了改变,必须记录下某一些位置的节点,这些情况就必须清楚的把需要的节点都规划出来,否则操作会非常繁琐易错。

1.1子区间反转
  • 我们可以设计一个子函数,将传入的head至tail之间的链表节点全部反转,并且返回反转之后的头尾节点。

  • 这里设计的变量有:

    • p:用来遍历节点,指的是当前操作的这个节点。
    • pre:指的是"前面那个节点",初始为空,开始反转之后,在节点p操作完成后,p成为了新的pre,供下一个p连接。
  • 为了同时返回两个变量,使用了STL中的pair,本函数可以返回一个以原本的tail为头节点,反转了顺序的、长度为K的链表,末尾为nullptr

  •     pair<ListNode*, ListNode*> reverseChildGroup(ListNode* head,ListNode* tail){
            ListNode *pre=nullptr;
            ListNode *p=head;//初始,"前面那个节点"是空节点
            while(pre!=tail){
                ListNode *temp=p->next;
                p->next=pre;
                pre=p;
                p=temp;
            }
            return {tail,head};
        }
    
1.2每K个反转
  • 上面用子函数解决了K个之内的反转,接下里就要解决K个内部反转之后怎么正确连接节点的问题。

  • 同样,关键仍然是清楚的规划好变量,把复杂问题条理化:

    • head_now、tail_now:这里的两个变量用于记录当前K区间的头尾位置。在反转之后,我们用这两个变量记录反转后的新head、tail。
    • 下面两个非常关键,通过他们完成了区间之间的正确连接,但是要理解他们的意义,明白什么时候应该更新他们的数值:
    • tail_pre:上一个K区间,反转后的尾部节点,用于和当前K区间的头节点连接(K区间的头节点可能是反转后的新头节点,也可能因为本区间数量不足K个因而没有反抓,需要在函数中分类讨论)。
      • 初始值:应当是哑巴节点,我习惯设为result。
      • 变化:在当前K区间反转后发生变化,新值为反反转函数返回的新tail。
    • head_now:因为我们每向前走K个节点,就需要返回来进行反转,此时会丢失下一个K区间开头的位置。因此我们需要在反抓前,就把下一个K区间开头的节点记录下来,以供使用。
      • 初始值:就是题目输入的head节点。
      • 变化:反转函数前,是反转前tail_now的下一个元素。
  • class Solution {
    public:
        pair<ListNode*, ListNode*> reverseChildGroup(ListNode* head,ListNode* tail){
            ListNode *pre=nullptr;
            ListNode *p=head;//初始,"前面那个节点"是空节点
            while(pre!=tail){
                ListNode *temp=p->next;
                p->next=pre;
                pre=p;
                p=temp;
            }
            return {tail,head};
        }
    
        ListNode* reverseKGroup(ListNode* head, int k) {
            ListNode *result,*p,*head_now,*tail_now,*head_next,*tail_pre;
            result=new ListNode();
            result->next=head;
            tail_pre=result;
            head_next=head;
            while(head_next!=nullptr){
                head_now=head_next;
                tail_now=head_next;
                for(int i=0;i<k-1;i++){
                    tail_now=tail_now->next;
                    if(tail_now==nullptr){
                        //注意因为上面的反转函数,tail->next总是nullptr
                        //如果最后一部分不满K个,则不反转,要把前一个区间的尾部正确连接。
                        tail_pre->next=head_now;
                        return result->next;
                    }
                }
                head_next=tail_now->next;//反转操作会让我们找不到下一个区间的入口,这里保留下来
                pair<ListNode*, ListNode*> pair1=reverseChildGroup(head_now,tail_now);
                head_now=pair1.first;
                tail_now=pair1.second;//head_now、tail_now反转后更新
                tail_pre->next=head_now;//本区间已经完成反转,可以将上一区间尾部连接到本区间
                tail_pre=tail_now;
            }
            return result->next;
        }
    };
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值