大家好,这篇文章给大家分享 LeetCode 148 排序链表 的最简可 AC 解法,使用归并排序,代码短、思路清晰、面试必背。
题目简介
给你链表的头结点 head,请将其按升序排列并返回排序后的链表。
要求:O(n log n) 时间复杂度,链表结构不支持随机访问,归并排序是最优解。
核心思路
- 快慢指针找中点:把链表切成两段
- 递归排序左右子链
- 合并两个有序链表
三步循环,最终得到完整有序链表。
完整代码(可直接提交)
// 链表排序:归并排序(递归版)
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,避免头节点判断麻烦
- 双指针遍历
l1和l2,每次把更小的节点接在结果后面 - 最后把剩余未遍历完的链表直接接上,完成合并