原题
链表中等1 种解法

#148排序链表

使用适合链表的归并排序,在 O(n log n) 时间内按升序排列节点。

#链表#归并排序#双指针

原题

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表

示例 1:

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

示例 2:

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

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [0, 5 * 104] 内
  • -105 <= Node.val <= 105

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

查看原题

解题主线

  1. fast 从 head.next 出发,使偶数长度时 slow 停在前半段尾节点,确保能正确断链。
  2. 链表合并只修改 next,不需要数组搬移元素。

解法 1:自顶向下归并排序

快慢指针二分链表,分别递归排序左右半段,再线性合并。

  • 时间复杂度: O(n log n)

  • 空间复杂度: O(log n)(递归栈)

JAVA
public class Solution {
    static final class ListNode {
        int val;
        ListNode next;
        ListNode(int val) { this.val = val; }
    }

    public ListNode sortList(ListNode head) {
        // 长度小于 2 的链表天然有序,也是递归能够收敛的边界。
        if (head == null || head.next == null) return head;

        ListNode slow = head;
        ListNode fast = head.next;
        // fast 从第二个节点出发,使 slow 最终停在前半段尾节点。
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        ListNode rightHead = slow.next;
        // 断开左右半段,确保两次递归的输入规模都严格缩小。
        slow.next = null;

        return merge(sortList(head), sortList(rightHead));
    }

    private ListNode merge(ListNode left, ListNode right) {
        ListNode dummy = new ListNode(0);
        ListNode tail = dummy;
        while (left != null && right != null) {
            if (left.val <= right.val) {
                tail.next = left;
                left = left.next;
            } else {
                tail.next = right;
                right = right.next;
            }
            tail = tail.next;
        }
        tail.next = left != null ? left : right;
        return dummy.next;
    }
}

边界与易错点

  • 若 fast 与 slow 都从 head 出发且直接在 slow 后断链,两节点输入可能无法缩小递归规模。
  • 自顶向下归并会占用 O(log n) 递归栈,不能标为严格 O(1) 额外空间。

整理来源

由旧仓库源码复核、去重并整理;展示代码已按 Java 21 语义修正明显问题。

  • Q148_linkedList_sort_list.java