当前位置 博文首页 > FMC_WBL的博客:LeetCode25. K 个一组翻转链表

    FMC_WBL的博客:LeetCode25. K 个一组翻转链表

    作者:[db:作者] 时间:2021-08-28 18:46

    给你一个链表,每?k?个节点一组进行翻转,请你返回翻转后的链表。

    k?是一个正整数,它的值小于或等于链表的长度。

    如果节点总数不是?k?的整数倍,那么请将最后剩余的节点保持原有顺序。

    ?

    示例:

    给你这个链表:1->2->3->4->5

    当?k?= 2 时,应当返回: 2->1->4->3->5

    当?k?= 3 时,应当返回: 3->2->1->4->5

    ?

    说明:

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

    /**
     * @author CAI
     * @date 2020/11/12-9:07
     */
    public class t25_k个一组翻转链表 {
    
        /**
         * 时间复杂度O(n),空间复杂度O(1) 假设;head=[1,2,3,4,5] ,k=2
         *
         * @param head 链表实例头结点
         * @param k    翻转的K数
         * @return
         */
        public ListNode reverseKGroup0(ListNode head, int k) {
            if (head == null || head.next == null) {
                return head;
            }
    
            // 创建一个空节点[0]
            ListNode dummy = new ListNode(0);
            // 将新创建的空结点 指向head节点[0 -> 1,2,3,4,5],此时dummy=[0,1,2,3,4,5]
            dummy.next = head;
    
            // 设置两个记录指针,此时pre=[0,1,2,3,4,5],此时end=[0,1,2,3,4,5]
            ListNode pre = dummy;
            ListNode end = dummy;
    
            // 遍历链表
            while (end.next != null) {
                // 这里设计的很巧妙,遍历k次的end,使end最终等于需要交换的节点,此时end=[2,3,4,5]
                for (int i = 0; i < k && end != null; i++) {
                    end = end.next;
                }
                // end如果为null,就跳出链表的遍历,证明已经把所有需要的节点交换完毕
                if (end == null) {
                    break;
                }
    
    
                // 这是创建两个交换指针 ,start=[1,2,3,4,5] next=[3,4,5]
                // 记录要翻转链表的头节点
                ListNode start = pre.next;
                // 记录下end.next,方便后面链接链表
                ListNode next = end.next;
                // 将end下一个指向null,目的是断开链表,end=[2,null ...]同时start=[1,2,null ...] ,就是end=[2],start=[1,2],
                end.next = null;
    
                // 拿到start需要反转的链表开始反转,此时执行完反转方法 pre=[0,2,1],dummy=[0,2,1],head=[1],start=[1],end=[2,1] , next=[3,4,5]
                pre.next = reverse(start);
    
                // 翻转后头节点变到最后。通过.next把断开的链表重新链接。start=[1,3,4,5],此时pre=[0,2,1,3,4,5],dummy=[0,2,1,3,4,5],end=[2,1,3,4,5] , next=[3,4,5]
                start.next = next;
    
                // 将pre换成下次要翻转的链表的头结点的上一个节点。即start pre=[1,3,4,5]
                pre = start;
    
                // 翻转结束,将end置为下次要翻转的链表的头结点的上一个节点。即start end=[1,3,4,5] , 其余同理(遍历第二遍dummy=[0,2,1,4,3,5],pre=[3,5]end=[3,5])
                end = pre;
            }
            // 返回链表头结点
            return dummy.next;
        }
    
        /**
         * 反转链表,此时的head是需要反转的链表
         */
        private ListNode reverse(ListNode head) {
            //单链表为空或只有一个节点,直接返回原单链表
            if (head == null || head.next == null) {
                return head;
            }
    
            // 创造当前节点的前一个节点
            ListNode pre = null;
            // 当前节点指针,此时curr就是[1,2]
            ListNode curr = head;
    
            // 遍历当前节点的链表
            while (curr != null) {
                // 记录指向下一个节点,保存当前节点后面的链表,next=[2] ; 第二次循环next=null
                ListNode next = curr.next;
                // 将curr当前节点next域指向前一个节点pre,此时curr=[1],pre=null,head=[1] ; 第二次循环next=null,curr=[2,1]
                curr.next = pre;
                // pre指针向后移动,此时pre只想当前节点 pre=[1] ; 第二次循环pre=[2,1]
                pre = curr;
                // cur指针向后移动。下一个节点变成当前节点 curr=[2],pre=[1],head=[1] ; 第二次循环curr = null,pre=[2,1],head=[1]
                curr = next;
            }
            // 返回已交换链表的头结点
            return pre;
        }
    
        /**
         * 递归,时间复杂度O(n),空间复杂度O(n)
         *
         * @param head 链表实例头结点
         * @param k    翻转的K数
         * @return
         */
        public ListNode reverseKGroup(ListNode head, int k) {
            if (head == null || head.next == null) {
                return head;
            }
    
            // 设置递归尾巴
            ListNode tail = head;
    
            // 找到待翻转的k个节点
            for (int i = 0; i < k; i++) {
                //剩余数量小于k的话,则不需要反转。
                if (tail == null) {
                    return head;
                }
                tail = tail.next;
            }
    
            // 反转前 k 个元素,将上一轮翻转后的尾结点指向下一轮翻转后的头节点,即将每一轮翻转的k的节点连接起来。
            ListNode newHead = reverse(head, tail);
    
            // 下一轮的开始的地方就是tail,对下一轮 k 个节点也进行翻转操作。
            head.next = reverseKGroup(tail, k);
    
            // 返回
            return newHead;
        }
    
        /**
         * @param head 整个未反转的链表
         * @param tail 本轮不需要反转的链表
         * @return
         */
        private ListNode reverse(ListNode head, ListNode tail) {
            ListNode pre = null;
            ListNode next = null;
    
            // 对其进行翻转。并返回翻转后的头结点
            while (head != tail) {
                // 每次都将当前节点的指针反转
    
                // 报错为反转的节点
                next = head.next;
    
                // 待反转的节点指向pre(pre会向后移动的)
                head.next = pre;
                // pre重新指向当前头结点
                pre = head;
    
                // head向后移动到未反转的节点
                head = next;
            }
            return pre;
        }
    
        /**
         * Definition for singly-linked list.
         */
        class ListNode {
            int val;
            ListNode next;
    
            ListNode() {}
    
            ListNode(int val) { this.val = val; }
    
            ListNode(int val, ListNode next) {
                this.val = val;
                this.next = next;
            }
        }
    }
    
    

    ?

    cs