LeetCode 25.K 个一组翻转链表

【1】题目描述:

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

在这里插入图片描述
【2】解题思路:

  • 先定义 startend 引用 分别指向k个一组中要被反转的第一个和最后一个,然后进行反转
    这里end通过下面定义的getKGroupEnd()方法找到
  • 然后在定义 LastEnd 指向上一组的最后一个,用来将上一组的最后一个 与当前组的第一个相连接,具体可见reverseKGroup()方法
  • 然后通过while循环不断寻找 下一个k组 然后反转上一个组连接当前组,然后继续寻找下一组
    直到发现 下一组的第一个为null就结束,或者发现下一个组的end为null 就代表下一个组数量不足k也不反转,那么就结束
  • 此题难点在于
  • 1.如何通过lastEnd 将上一组与下一组连接
  • 2.如果根据start和K寻找当前组最后一个要反转的元素即end。这里要注意边界条件。
  • 对于难点1:在下面代码可见详细解释
    对于难点2:

/*
* 题目中说K个一组,假设K等于3 假设第一个为0 那么就是 0 1 2 进行反转 那么如何找到 2呢?
* 从 0 到 2相当于是 0到1 1到2 跳转两次,一般化来说 K个一组 要从第一个到第K个 那么就跳转 k-1次
* 因此就变成 循环K-1次 这样来设计 while循环的结束条件。
* 法一:while(k!=1){k–}; 相当于循环了k-1次
* 分析: k-1
* k-2
* k-3
* …
* k-(k-2)=2
* k-(k-1)=1 此时结束循环,一共循环了{(k-1)-1+1}=k-1次
* 总结:while(k!=i){k–};这种循环可总结为 循环了 k-i次
* 法二:while(–k!=0){};相当于循环了k-1次
* 分析:–k相当于是先减 然在吧减完之后的k值 进行 比较运算
* 第一次判断 k-1
* k-2
* k-3
* …
* k-(k-2)=2
* k-(k-1)=1
* k-k=0 此时不进行while{}里面的语句,因此实际上循环了 上面的k-1次
* 法二与法一对比:法二是先减在判断,而法一是先判断在减,因此法一在进行一次循环后它的k才变为k-1,
* 而法二没进行第一次循环k就已经变成了k-1。因此在最后结束条件那里,法一先循环在减,而法二先判断在减,
* 如果它俩的结束条件一样,那么法一将比法二多循环一次。
* 假如结束条件为 K!=0那么法一将循环k次 法二循环k-1次
* 因此对于此题 要求循环k-1次 所以法一结束条件为 k!=1
* 法二结束条件为 k!=2
*
* 总结:while(–k!=i){};这种循环可总结为 循环了 k-i-1

【3】代码:

package ll_04_ReverseNodesInK_Gropu;

/**
 * @Auther: 米兰的小铁匠
 * @Date: 2022/4/19 12:34
 * @Description: K个结点的组内逆序调整
 * 来源:LeetCode 25.K 个一组翻转链表
 * 给你链表的头节点 head,每K个节点一组进行翻转,请你返回修改后的链表。
 * k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是k的整数倍,那么请将最后剩余的节点保持原有顺序。
 * 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
 */
@SuppressWarnings("all")
public class reverseKGroup {
    /*
     * 解题思路:先定义 start 和 end 引用 分别指向k个一组中要被反转的第一个和最后一个,然后进行反转
     * end通过下面定义的getKGroupEnd()方法找到
     * 然后在定义 LastEnd 指向上一组的最后一个,用来将上一组的最后一个 与当前组的第一个想连接,具体可见reverseKGroup()方法
     * 然后通过while循环不断寻找 下一个k组 然后反转上一个组连接当前组,然后继续寻找下一组
     * 直到发现 下一组的第一个为null就结束,或者发现下一个组的end为null 就代表下一个组数量不足k也不反转,那么就结束。
     *
     * 此题难点在于 1.如何通过lastEnd 将上一组与下一组连接
     *             2.如果根据start和K寻找当前组最后一个要反转的元素即end。这里要注意边界条件。
     * */

    public ListNode reverseKGroup(ListNode head, int k) {
        ListNode start = head;//start代表当前组反转组的第一个
        ListNode end = getKGroupEnd(start, k);//end 代表当前反转组的最后一个
        if (end == null) { //如果当前组元素不足k个,那么end将跑到null,具体可见getKGroupEnd(start,k)方法
            return head;
        }
        head = end; //在第一组反转完毕后,第一组的end所指向的将永远都是此链表的第一个
        reverse(start, end);//反转第一组链表,反转完毕后 start的下一个将指向下个组的第一个,具体可见reverse方法
        //定义lastEnd 代表上一组的最后一个,而stat代表上一组的最后一个,因此就让lastEnd 指向start
        ListNode lastEnd = start;

        /*
         * lastEnd代表上一组的最后一个,
         * lastEnd.next则表示下面要反转组的第一个
         * lastEnd.next为空就表示下面要反转组的第一个为空表明已经不能在反转了,因此就结束循环
         * */
        while (lastEnd.next != null) {
            /*
             * lastEnd代表上组的最后一个,lastEnd.next表示下一组的第一个
             * 因此就让start更新为下一组的第一个即指向lastEnd.next
             * 然后再让根据当前的start和k找到这组的end
             * 如果发现 end找到的为空,就说明此组元素的数量不足k个,然后end跑到了null,具体可见getKGroupEnd方法
             * 因此表示此组不用反转,次链表反转k一个组反转完毕,因此直接返回head
             * */
            start = lastEnd.next;
            end = getKGroupEnd(start, k);
            if (end == null) {
                return head;
            }
            reverse(start, end);//进行反转,反转完毕后start变成当前组最后一个,并且它指向下一个组第一个

            /*
             * lastEnd此时指向上一组的最后一个
             * 由于反转完毕,此时lastEnd.next指向了当前组的最后一个,原本指向的是第一个
             * 因为要将上一个组与下一个组相连接起来.因此需要将上一组的最后一个连接到当前组第一个即end
             * 因此就要让lastEnd.next指向end 即 lastEnd.next = end;此时上一组的最后一个与当前组第一个就连接起来了
             * 此时在更新lastEnd的位置,让它指向当前组的最后一个即当前组的start位置
             * 此时lastEnd指向了当前组的最后一个位置,并且上一组的最后一个也与当前组第一个想连接起来了
             * */
            lastEnd.next = end;
            lastEnd = start;
        }

        //while结束后,表示链表K个一组反转完毕,就returnhead
        return head;
    }

    //寻找每个组内最后一个节点,即这个组从第一个到最后一个进行反转
    public static ListNode getKGroupEnd(ListNode start, int k) {
        /*
         * 题目中说K个一组,假设K等于3 假设第一个为0 那么就是 0 1 2 进行反转 那么如何找到 2呢?
         * 从 0 到 2相当于是 0到1 1到2 跳转两次,一般化来说 K个一组 要从第一个到第K个 那么就跳转 k-1次
         * 因此就变成 循环K-1次 这样来设计 while循环的结束条件。
         * 法一:while(k!=1){k--}; 相当于循环了k-1次
         *                           分析: k-1
         *                                 k-2
         *                                 k-3
         *                                 ...
         *                                 k-(k-2)=2
         *                                 k-(k-1)=1 此时结束循环,一共循环了{(k-1)-1+1}=k-1次
         * 总结:while(k!=i){k--};这种循环可总结为 循环了 k-i次
         * 法二:while(--k!=0){};相当于循环了k-1次
         *                        分析:--k相当于是先减 然在吧减完之后的k值 进行 比较运算
         *                        第一次判断 k-1
         *                                  k-2
         *                                  k-3
         *                                  ...
         *                                  k-(k-2)=2
         *                                  k-(k-1)=1
         *                                  k-k=0  此时不进行while{}里面的语句,因此实际上循环了 上面的k-1次
         * 法二与法一对比:法二是先减在判断,而法一是先判断在减,因此法一在进行一次循环后它的k才变为k-1,
         * 而法二没进行第一次循环k就已经变成了k-1。因此在最后结束条件那里,法一先循环在减,而法二先判断在减,
         * 如果它俩的结束条件一样,那么法一将比法二多循环一次。
         * 假如结束条件为 K!=0那么法一将循环k次 法二循环k-1次
         * 因此对于此题 要求循环k-1次 所以法一结束条件为 k!=1
         *                              法二结束条件为 k!=2
         *
         * 总结:while(--k!=i){};这种循环可总结为 循环了 k-i-1
         *
         * 此题先采用法二来设计while循环的结束条件
         * */
        while (--k != 0 && start != null) {//如果 循环次数  没到且start当前所指的不为空,就进行当前这趟循环
            start = start.next;
        }
        return start;
    }


    //从start所指的节点到end所指的节点进行反转,并且最后start.next指向原先end.next即原先end的下一个
    public static void reverse(ListNode start, ListNode end) {
        end = end.next;//end指向它的下一个即下个k组的第一个元素
        ListNode pre = null;
        ListNode cur = start;
        ListNode next = null;
        //此前反转链表都是 cur != null时 进行反转。现在就吧 end当成 null
        while (cur != end) {
            next = cur.next;
            cur.next = pre; //cur所指向的节点完成反转
            pre = cur; //pre后移
            cur = next;//cur后移
        }
        /*
         * 当前组反转完毕后,start所指向的就是当前组的最后一个,那么它next就要指向下一组的第一个
         * 而end此时指向的就是下一个组的第一个元素
         * 因此 就让start.next = end; 即 反转后的这一组的最后一个 连接到 下一个组的第一个
         * */
        start.next = end;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值