LeetCode 148. 排序链表|极简归并排序写法

1 阅读2分钟

大家好,这篇文章给大家分享 LeetCode 148 排序链表 的最简可 AC 解法,使用归并排序,代码短、思路清晰、面试必背。

题目简介

给你链表的头结点 head,请将其按升序排列并返回排序后的链表。 要求:O(n log n) 时间复杂度,链表结构不支持随机访问,归并排序是最优解。

核心思路

  1. 快慢指针找中点:把链表切成两段
  2. 递归排序左右子链
  3. 合并两个有序链表

三步循环,最终得到完整有序链表。

完整代码(可直接提交)

// 链表排序:归并排序(递归版)
var sortList = function(head) {
    // 递归终止条件:空节点 or 单个节点
    if (!head || !head.next) return head;

    // 快慢指针找链表中点
    let slow = head, fast = head.next;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
    }

    // 切分链表 → 左:head~slow,右:mid~结束
    const mid = slow.next;
    slow.next = null;

    // 递归 + 合并
    return merge(sortList(head), sortList(mid));
};

// 合并两个有序链表(通用工具函数)
function merge(l1, l2) {
    const dummy = new ListNode();
    let p = dummy;

    // 双指针有序合并
    while (l1 && l2) {
        if (l1.val < l2.val) {
            p.next = l1;
            l1 = l1.next;
        } else {
            p.next = l2;
            l2 = l2.next;
        }
        p = p.next;
    }

    // 接上剩余部分
    p.next = l1 || l2;
    return dummy.next;
}

代码逐行解释

1. sortList 主函数

  • 终止条件:链表为空或只有一个节点,天然有序。
  • 快慢指针
    • slow 每次走一步
    • fast 每次走两步
    • 循环结束时,slow 指向链表中点
  • 切分链表slow.next = null 把链表一分为二,方便递归。
  • 归并:分别排序左右两段,再合并成一个有序链表。

2. merge 合并函数

  • 使用虚拟头节点 dummy,避免头节点判断麻烦
  • 双指针遍历 l1l2,每次把更小的节点接在结果后面
  • 最后把剩余未遍历完的链表直接接上,完成合并