将链表按照左右分区重新排列

将链表按照左右分区重新排列

问题重述:

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
https://leetcode-cn.com/problems/reorder-list/

示例 1:

img

输入:head = [1,2,3,4]
输出:[1,4,2,3]

示例 2:

img

输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]

问题分析:

题目要求按照左右分区来重新排列链表,并且将右半区的结点倒序间隔的插入左半边链表中,因为我们链表只能从前往后,不能实现倒序,所以我们可以想到将右半边链表反转,然后插入到左边。那么如何将将链表分区呢,我们可以想到使用快慢指针,快慢指针很容易就可以找到中间结点

解法:

双指针

解题:

代码:
 public ListNode reorderList(ListNode head) {
        /**
         * 主要分为三个步骤
         * 首先找到中间节点
         * 将右边链表反转
         * 将反转后的链表和左半边的链表连起来
         */
         // 特殊情况
         if(head == null || head.next == null){
             return head;
         }
         // 使用快慢指针找到中间节点
         ListNode dummy = new ListNode(-1,head);
         ListNode slow = dummy;
         ListNode fast = dummy;
         while(fast.next != null && fast.next.next != null){
             slow = slow.next;
             fast = fast.next.next;
         }
         // 此时的slow指向的是中间结点的前一个结点,反转
         fast = reverse(slow.next);
         head = mergeLR(head,fast);
        return head;
    }
    public ListNode reverse(ListNode head){
        // 反转链表
        ListNode pre = null;
        ListNode cur = head;
        ListNode next = null;
        while(cur != null){
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
    public ListNode mergeLR(ListNode left,ListNode right){
        // 两个链表的合并
        // 当我们合并的时候,右边结点的next需要被保存哟关于下一次使用
        ListNode rNext = null;
        // 创建一个结点用于遍历链表
        ListNode cur = left;
        while(cur != null && right != null){
            // 保存当前右边结点的下一个结点,用于下一次使用
            rNext = right.next;
            // 将右边结点插入到左边去
            right.next = cur.next;
            cur.next = right;
            // 因为此时cur的next插入了right,所以需要向右移动两格
            cur = cur.next.next;
            // 更新right结点
            right = rNext;
        }
        // 这里非常需要注意,这里如果不对当前结点的next进行处理的话会造成结点成环
        cur.next = rNext;
        return left;
    }

代码解析:通过问题分析我们可以知道这道题的基本思路,其中有几个细节需要注意:首先是我们要使用快慢指针操作链表的时候,最好创建一个假的dummy结点辅助操作,这样有助于对头节点的操作(也就是说快慢指针不能同时从头节点开始),否则容易造成其他后果。还有一个细节就是在将左右两段链表交叉连接起来的时候,需要注意最后一个结点,此时循环完成后,最后一个结点是中间节点,根据循环中的操作,此时该节点的next是它本身(因为right.next = cur.next而cur.next就是当前结点right),所以此时如果不对这个结点进行处理的话,这会成为一个环。对该结点的操作就是将原本右边可能有的结点连接到该节点的next(本题中右边已经没有了结点,但是做到其他题目的话,可能右边还会有结点,连起来即可)

总结:

当看到链表+中间,就要想到快慢指针,使用快慢指针的时候需要注意不能同时从head结点开始,最好是创建一个假节点放到头节点前面,然后从前面开始。

对于链表内结点的相互连接的时候,需要注意结点连接后是否会成环,主要是考虑最后一个结点。

posted @ 2022-02-23 19:00  foldn  阅读(75)  评论(0)    收藏  举报