LeetCode 143. Reorder List

问题描述

  • Given a singly linked list L: L0→L1→…→Ln-1→Ln,
    reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
    You must do this in-place without altering the nodes’ values.
    For example,
    Given {1,2,3,4}, reorder it to {1,4,2,3}.
  • 地址

问题分析

  • 分为三步:

    • 找到中间节点(实际上是前部分链表的末尾节点),可以将链表“一分为二”
      • 如何 one pass找到中间节点呢? 快慢双指针
        建议是利用快慢指针找到前半部分链表的末尾节点,因为利用链尾节点,可以很方便找后半部分的链头。例如对于 1 -> 2 -> 3 -> 4 而言,slow 最终定位到 2,而对于 1 -> 2 -> 3 -> 4 -> 5 而言, slow 最终定位到 3,所以这种一分为二的方法,前半部分比后半部分等长或者大于1。
    • 将后半部分链表反转

      • 利用头插法。类似于LeetCode 92. Reverse Linked List II 中的方法。
      • 具体如下,每次将 4后面的节点连在 3的后面,然后4取代后面节点的位置,此循环,直至4到末尾。因为反转链表前一定有节点(3),所以无需 dummy 节点
        • 1 -> 2 -> 3 -> 4 -> 5 -> 6
        • 1 -> 2 -> 3 -> 5 -> 4 -> 6
        • 1 -> 2 -> 3 -> 6 -> 5 -> 4
    • 将前后链表合并

      • 方法1: 将两部分链表分开,然后一一合并,最终连上前部分多的那部分
      • 方法2: 直接原地调整,具体见代码

经验教训

  • 如何one pass 找到链表的中间节点?
    • 最好slow定位到前部分末尾节点
//利用快慢指针,最终时slow指向前一半链表的末尾
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
  • 如何反转部分链表?
    • 类似头插法
  • 如何合并两链表
    • 注意 tail 的变化

代码实现

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void reorderList(ListNode head) {
        if (head == null || head.next == null || head.next.next == null ) {
            return;
        }
        //利用快慢指针,最终时slow指向前一半链表的末尾
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        //利用头插法,将后一半链表进行反转
        ListNode preList = slow;
        ListNode curNode = slow.next;
        while (curNode.next != null) {
            ListNode insertNode = curNode.next;
            curNode.next = insertNode.next;
            insertNode.next = preList.next;
            preList.next = insertNode;
        }
        //将前部分和后部分左右相间连在一起(不分开)
        //这种相连方法只适用于两链等长或者前者比后者大1
        ListNode part1 = head;
        ListNode part2 = preList.next;
        while (part1 != preList) {
            preList.next = part2.next;
            part2.next = part1.next;
            part1.next = part2;
            part1 = part2.next;
            part2 = preList.next;
        }
        //以下是前后部分先分成两条链表,再连
        /*
        preList.next = null;
        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;
        while (part1 != null && part2 != null) {
            tail.next = part1;
            //注意此刻便更新!!!
            part1 = part1.next;
            tail.next.next = part2;
            part2 = part2.next;
            tail = tail.next.next;
        }
        //因为前部分长度有可能比后部分大1(整体长度为奇数),所以再把它跟在后面
        tail.next = part1;
        */
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值