zoukankan      html  css  js  c++  java
  • 【LeetCode-链表】K个一组反转链表

    题目描述

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

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

    题目链接: https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

    思路1

    对链表每 k 个节点执行一次普通的链表反转,过程如下图

    图来自这里

    具体代码如下:

    /**
     * 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) {
            if(head==nullptr || k<2) return head;
    
            ListNode* dummy = new ListNode(0);
            dummy->next = head;
            ListNode* preTail = dummy;  // 前一段链表的链表尾
            ListNode* curTail = dummy;  // 当前这一段反转前的链表尾,反转后的链表头
            while(curTail->next!=nullptr){
                int cnt = 0;
                while(cnt<k && curTail!=nullptr){
                    curTail = curTail->next;
                    cnt++;
                }
                if(curTail==nullptr) return dummy->next;
    
                ListNode* curHead = preTail->next; // 当前这一段反转前的链表头,反转后的链表尾
                ListNode* next = curTail->next;
                curTail->next = nullptr;  // 别忘了这一句
                preTail->next = reverse(curHead);
                curHead->next = next;
                preTail = curHead;
                curTail = curHead;
            }
            return dummy->next;
        }
    
        /*单链表反转*/
        ListNode* reverse(ListNode* head){
            if(head==nullptr) return head;
    
            ListNode* pre = nullptr;
            ListNode* cur = head;
            ListNode* next = nullptr;
            while(cur!=nullptr){
                next = cur->next;
                cur->next = pre;
                pre = cur;
                cur = next;
            }
            return pre;
        }
    };
    

    思路2

    思路 2 是我最初的做法,和思路 1 大致相似,但是写的不太好理解,推荐思路 1 或者思路 3.

    根据最基础的链表反转来做。因为题目要求如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序,所以先求要翻转几段链表(链表长度/k),然后分别对每段子链表应用链表翻转,需要注意的是反转后指针的变化,要防止链表断裂,具体代码如下:

    /**
     * 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) {
            if(head==nullptr || head->next==nullptr || k==1){
                return head;
            }
    
            ListNode* newHead = new ListNode(0);    // 新的链表头
            newHead->next = head;
            ListNode* pre = newHead;    // 存储前一段链表反转后的尾结点,用于连接各段链表,防止链表断裂
            ListNode* preNode = nullptr;    // 用于反转链表节点
            ListNode* curNode = head;
            ListNode* nextNode = nullptr;
            int length = getListLength(head);   // 获取链表长度
            int loop = length/k;    // 要反转loop段长为k的链表
            while(loop){
                ListNode* firstNode = curNode;  // 链表反转, firstNode记录链表反转前的第一个节点(反转后最后一个节点,用于连接链表段,防止链表断裂)
                for(int i=1; i<=k && curNode!=nullptr; i++){
                    nextNode = curNode->next;
                    curNode->next = preNode;
                    preNode = curNode;
                    curNode = nextNode;
                }
    
                pre->next = preNode;  //将前一段反转后链表的链表尾和当前这一段相连
                firstNode->next = curNode;  // 将当前这一段的反转后的链表尾指向下一段的未反转的链表头
                pre = firstNode;    // 更新pre为反转后链表的尾结点
                loop--;   
            }
            return newHead->next;
        }
    
        int getListLength(ListNode* head){
            int length = 0;
            while(head!=nullptr){
                head = head->next;
                length++;
            }
            return length;
        }
    };
    
    • 时间复杂度:O(n)
      遍历链表一遍。
    • 空间复杂度: O(1)

    思路3

    使用栈来做。将节点入栈后出栈再连接就是反序了。用栈也要注意,最后一段链表的节点个数可能不够 k 个,这个时候就不需要出栈反转了,只需要记录下前一段链表的链表尾 preTail 和当前段链表的链表头 partHead,然后 preTail->next = partHead 即可。

    /**
     * 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) {
            if(head==nullptr || k<2) return head;
    
            stack<ListNode*> s;
            ListNode* dummy = new ListNode(0);
            dummy->next = head;
            ListNode* cur = head;
            ListNode* partHead;  // 当前这一段反转前的链表头
            ListNode* preTail = dummy;  // 前一段的链表尾
            while(cur!=nullptr){
                int cnt = 0;
                partHead = cur;
                while(cnt<k && cur!=nullptr){
                    s.push(cur);
                    cur = cur->next;
                    cnt++;
                }
    
                if(s.size()==k){
                    while(!s.empty()){
                        ListNode* node = s.top(); s.pop();
                        preTail->next = node;
                        preTail = preTail->next;
                        node->next = nullptr;  // 别忘了这一句
                    }
                }else preTail->next = partHead;  // 最后一段不够 k 个,直接连接
            }
            return dummy->next;
        }
    };
    

    总结

    和链表反转的题目都可以根据最基础的链表反转这题的思想来做。代码如下:

    class Solution {
    public:
        ListNode* reverseList(ListNode* head) {
            if(head==nullptr || head->next==nullptr){
                return head;
            }
    
            ListNode* preNode = nullptr;
            ListNode* curNode = head;
            ListNode* nextNode = nullptr;
            while(curNode!=nullptr){
                nextNode = curNode->next;
                curNode->next = preNode; 
                preNode = curNode;
                curNode = nextNode;
            }
            return preNode; // 注意返回的是preNode,因为curNode此时为nullptr
        }
    };
    

    这样反转链表后有两个非常有用的性质:1、反转结束后preNode就是反转后链表的头结点;2、反转结束后curNode是反转结束后preNode原先指向的那个节点,。(如果把整个链表分成多段分别翻转,这个性质就非常有用,比如当前的这一题“K个一组反转链表”),如下图

    相关题目

    1、反转链表:https://leetcode-cn.com/problems/reverse-linked-list/题解
    2、反转链表II:https://leetcode-cn.com/problems/reverse-linked-list-ii/题解

    参考

    1、https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/tu-jie-kge-yi-zu-fan-zhuan-lian-biao-by-user7208t/
    2、https://leetcode-cn.com/problems/reverse-nodes-in-k-group/solution/kge-yi-zu-fan-zhuan-lian-biao-by-powcai/

  • 相关阅读:
    EasyUi datagrid列表增加复选框
    List集合流处理类型小结
    MockMvc模拟对controller进行单元测试
    项目配置不带项目名称访问
    mongodb常用的sql语句总结
    git push时报错:Updates were rejected because the tip of your current branch is behind
    xml转json和实体类的两种方式
    win 10 关闭或打开 测试模式
    Wise Force Deleter 强制删除文件工具 ---- 亲测好用
    Win 10 你不能访问此共享文件夹,因为你组织的安全策略阻止未经身份验证的来宾访问....
  • 原文地址:https://www.cnblogs.com/flix/p/12682135.html
Copyright © 2011-2022 走看看