算法通过村第二关-链表黄金笔记|K个一组反转

文章目录

  • 前言
  • 链表反转|K个一组翻转链表
  • 解题方法:
    • 头插法处理:
    • 穿针引线法处理:
  • 总结


前言

提示:没有人天生就喜欢一种气味而讨厌另一种气味。文明的暗示而已。


链表反转|K个一组翻转链表

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

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

算法通过村第二关-链表黄金笔记|K个一组反转_第1张图片
进阶:你可以设计一个只用 O(1) 额外内存空间的算法解决此问题吗?

思路来的很快,重点是代码层面的编写,这个很重要,思路呢?就是常见的头插法和穿针引线法来处理数组反转问题,当然今天我们也是采用这两种方法解决的,那就开始实现他吧

解题方法:

头插法处理:

头插法重点在理解虚拟节点上,如果这个问题解决了,相比较而言要比穿针引线要好实现的多, 我们试着把链表整体分为3段,一段是已经翻转的,一端是正在反转,一端是未反转的。为了方便翻转,我们需要京链表遍历一边,统计一下链表的长度len,然后将链表进行分组n=len/k,接下来就是循环进行分组翻转链表。 我们尝试这画一些图,能够更有里的说明:
算法通过村第二关-链表黄金笔记|K个一组反转_第2张图片
结假设我们开始翻转 4 节点:
算法通过村第二关-链表黄金笔记|K个一组反转_第3张图片
具体思路:

  1. 找到链表的长度 len 分组
  2. cur.next = cur.next.next; next.next = pre.next; pre.next = next;
  3. 循环下一次

上代码

	/**
     * 方法2:头插法
     *
     * @param head
     * @param k
     * @return
     */
    public static ListNode reverseKGroup2(ListNode head, int k) {
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode cur = head;
        int len = 0;
        while (cur != null) {
            cur = cur.next;
            len++;
        }
        int n = len / k; // 计算出来分几组
        ListNode pre = dummyNode;
        cur = head;
        for(int i = 0; i < n; i++) {
            for(int j = 0; j < k - 1; j++) {  // ? k - 1  画图就知道了
                ListNode next = cur.next;
                cur.next = cur.next.next;
                next.next = pre.next;
                pre.next = next;
            }
            pre = cur;  
            cur = cur.next;   // 记得修改指针
        }
       return dummyNode.next;
    }

穿针引线法处理:

这个思路可以回顾一下穿针引线,到底是怎么回事

首先还是将链表分组翻转, 我们就可以一组一组的处理,把他们分成已经翻转的、正在翻转的和未翻转的三部分,同时为了方便处理头节点,我们使用了一个虚拟的节点。

接下来就是遍历,根据k个为一组找到四个关键位置,并使用变量per,start,end,next标记,比如下面的图:
算法通过村第二关-链表黄金笔记|K个一组反转_第4张图片
接着我们就开始翻转,对应颜色进行翻转,我们将end.next = null ,直接使用链表翻转,复用链表翻转的常规操作。注意指针的变化,head便是传入方法的参数,我们接着看图:
算法通过村第二关-链表黄金笔记|K个一组反转_第5张图片

当然翻转之后,我们接下来就是将原始链表缝起来,这就需要调整指针域,同样这里也要注意指针的变化
算法通过村第二关-链表黄金笔记|K个一组反转_第6张图片
接着调整指针进行下一次循环:
算法通过村第二关-链表黄金笔记|K个一组反转_第7张图片
总结一下

  1. 遍历找到四个关键位置,复用常规翻转链表
  2. pre.next = end; start.next = next; 调整指针域
  3. 调整下一次循环

了解上面的图,代码应该也会写吧

 /**
     * 方法1: 穿针引线法
     *
     * @param head
     * @param k
     * @return
     */
    public static ListNode reverseKGroup(ListNode head, int k) {
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;
        ListNode pre = dummyNode;
        ListNode end = dummyNode;
        while (end.next != null) { // 最终结束的地方
            for (int i = 0; i < k && end != null; i++) {
                end = end.next; // 找到当前分组的最后一个
            }
            if (end == null) {
                break;
            }
            // 找到start next
            ListNode start = pre.next;
            ListNode next = end.next;
            end.next = null; // 思考?
            pre.next = reverse(start);
            start.next = next;
            pre = end;

            // 调整下一次循环
            end = pre;
        }
        return dummyNode.next;
    }

复习一下链表反转:


 private static ListNode reverse(ListNode head) {
       ListNode pre = null;
       ListNode cur = head;
       while(cur != null) {
           ListNode next = cur.next;
           cur.next = pre;
           pre = cur;
           cur = next;
       }
       return pre;
    }

总结

注意:指针域的变化,多画图更容易理解,链表反转重点,重点,重点!!!

你可能感兴趣的:(算法集训营,算法,链表,笔记)