835 字
4 分钟
排序链表

题目描述#

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

解题思路#

  1. 归并排序框架
    • 使用快慢指针找到链表中点
    • 递归排序左半部分链表
    • 递归排序右半部分链表
    • 合并两个有序链表
  2. 迭代优化
    • 自底向上归并,避免递归栈空间
    • 按子链表长度倍增合并
  3. 关键操作
    • 链表分割时需断开中点连接
    • 合并过程使用哑节点简化操作
  4. 空间控制
    • 迭代法实现常数空间复杂度

关键洞察#

  1. 链表特性利用
    • 归并排序天然适合链表结构
    • 合并操作无需额外空间
  2. 中点定位技巧
    • 快指针走2步,慢指针走1步
    • 慢指针停在中点前驱位置
  3. 递归终止条件
    • 空链表或单节点链表已有序
  4. 迭代步进控制
    • 子链表长度从1开始倍增
    • 每次遍历完成整轮合并

复杂度分析#

方法时间复杂度空间复杂度
递归法O(n log n)O(log n) (递归栈)
迭代法O(n log n)O(1)

代码实现#

递归法

/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function (head) {
const toSortList = (head, tail) => {
if (!head) return head;
if (head.next === tail) {
head.next = null;
return head;
}
let fast = slow = head;
while (fast !== tail) {
slow = slow.next;
fast = fast.next;
if (fast !== tail) {
fast = fast.next;
}
}
let mid = slow;
return merge(toSortList(head, mid), toSortList(mid, tail))
}
const merge = (head1, head2) => {
const dummyHead = new ListNode(0);
let temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 !== null && temp2 !== null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next
} else {
temp.next = temp2;
temp2 = temp2.next
}
temp = temp.next;
}
if (temp1 !== null) {
temp.next = temp1
} else if (temp2 !== null) {
temp.next = temp2;
}
return dummyHead.next;
}
return toSortList(head, null);
};

迭代法

/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
const sortList = (head) => {
// 边界处理
if (!head || !head.next) return head;
// 获取链表长度
let n = 0, cur = head;
while (cur) {
n++;
cur = cur.next;
}
const dummy = new ListNode(-1, head);
// 子链表长度从1开始倍增
for (let size = 1; size < n; size *= 2) {
let prev = dummy;
let cur = dummy.next;
while (cur) {
// 获取左子链表
const left = cur;
const right = split(left, size);
cur = split(right, size); // 剩余部分
// 合并左右子链表
prev.next = merge(left, right);
// 移动prev到合并后链表的末尾
while (prev.next) {
prev = prev.next;
}
}
}
return dummy.next;
};
// 合并两个有序链表
const merge = (l1, l2) => {
const dummy = new ListNode(-1);
let cur = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) {
cur.next = l1;
l1 = l1.next;
} else {
cur.next = l2;
l2 = l2.next;
}
cur = cur.next;
}
cur.next = l1 || l2;
return dummy.next;
};
// 分割链表:返回后半部分头节点
const split = (head, size) => {
if (!head) return null;
// 移动size-1步找到分割点
for (let i = 1; i < size && head.next; i++) {
head = head.next;
}
const nextHead = head.next;
head.next = null; // 断开连接
return nextHead;
};

相似题目#

排序链表
https://website-truelovings-projects.vercel.app/posts/algorithms/排序链表/
作者
欢迎来到StarSky的网站!
发布于
2025-08-16
许可协议
CC BY-NC-SA 4.0