type
status
date
summary
tags
category
icon
password
featured
freq
difficulty
给出链表的头结点
head ,请将其按 升序 排列并返回 排序后的链表 。示例1:

示例2:

示例 3:
解法一:自顶向下归并排序- 递归
自顶向下归并就是我们平时最熟的 递归归并排序,只不过对象从数组换成了链表。
链表这种结构不支持随机访问,所有“拆分、合并”的操作本来就要通过指针走一遍,所以归并排序特别适合链表。
- 不断二分链表:
- 用快慢指针找到中间节点
mid:快指针一次走两步,慢指针一次一步;快指针到头时,慢指针在中间。 - 把链表从中间
slow.next = None切成左右两半:[head ... mid前]、[mid ... tail]。
- 递归地给左右两半分别排序:
- 递归到底的情况就是:空链表或只有一个节点,这种天然有序,直接返回。
- 合并两个有序链表:
- 完全复用 LeetCode 21:Merge Two Sorted Lists(合并两个有序链表)的思路
- 用两个指针分别指向 left / right,谁小就接到结果链表后面,指针往前走。
复杂度
- 时间:O(n log n),每一层归并是 O(n),总共 log n 层。
- 空间:O(log n),主要是递归栈。
解法二:自底向上归并排序 - 迭代(推荐)
和上面本质完全一样,都是归并排序,只是:
- 上面是先递归到底再往上合并(自顶向下);
- 这里是从长度为 1 的小段开始,一轮轮往上合并(自底向上)。
可以这么理解:
- 先知道链表总长度
length。
- 维护一个子链表长度
sub_len,初始为 1。
意思是:当前这一轮我们把“长度为 1 的小块”两两合并成“长度为 2 的块”。
- 然后下一轮
sub_len *= 2:长度为 2 的块,两两合并成长度为 4 的块。
- 一直翻倍,直到
sub_len >= length,整个链表就完全有序了。
每一轮里,我们从头开始,按当前的
sub_len 不断切出:- 一段
head1(长度 ~ sub_len)
- 一段
head2(长度 ~ sub_len)
- 把这两个有序段归并成一段长约
2 * sub_len的新有序段,然后接回大链表中。
复杂度
- 时间:O(n log n),每一层合并所有节点是 O(n),总共 log n 层。
- 空间:O(1),迭代 + 常量指针。
解法三:冒泡排序
冒泡排序在数组里就是不断把“最大值”冒到最后。放到链表里也一样,只不过通常只交换节点的值,不动指针。
- 外层循环:从头到尾跑很多趟;
- 内层循环:针对当前“未排序的部分”,一对一对比较相邻节点:
- 如果前面的值比后面大,就交换它们的
val; - 一趟下来,最大值就被“气泡”到了尾部。
链表版本和数组版本质一样,只是访问是用
next 而不是下标。复杂度
- 时间:O(n²),两层循环。n 偏大就会超时。
- 空间:O(1)。
解法四:选择排序
每一轮在“剩下的部分”中找一个最小值,把它放到当前开头。
node_i指向当前“未排序区间的第一个节点”;
- 从
node_i往后扫一遍,找到值最小的节点min_node;
- 把
node_i.val和min_node.val交换;
- 然后
node_i往后走一个,重复。
通常只交换
val,不调指针。复杂度
- 时间:O(n²),每次找最小值都要跑一遍后半部分。
- 空间:O(1)。
解法五:插入排序
- 前半部分维护为有序链表
sorted;
cur指向“下一个要插入的节点”;
- 如果
cur.val比sorted尾巴还大,那直接接在后面就行;
- 否则,需要从头(借助一个 dummy 头)往后找第一个值大于
cur.val的位置,把cur插到那前面。
链表版本的插入排序相对数组来说一个好处是:插入不需要挪动一大片元素,只需要改几个指针;
但整体仍然是“每个元素最坏寻找插入位置要 O(n)” → 总体 O(n²)。
非常适合“逐步维护一个有序前缀”的场景。
复杂度
- 时间:O(n²)。
- 空间:O(1)。
解法六:计数排序
- 遍历一遍链表,找到所有值的
min和max;
- 开一个
counts数组,长度约等于max - min + 1;
- 再遍历一遍链表,
counts[val - min]++统计频次;
- 最后按从小到大扫一遍
counts,把值按次数重新写回链表(偏常见的是新建节点,也可以原地重写val)。
这个解法比较“暴力”,但只要值域不是很大,时间可以接近 O(n)。
复杂度
- 时间:O(n + k),k 是值域大小。
- 空间:O(k),计数数组
- 作者:Fan Luo
- 链接:https://fanluo.me/article/leetcode148-链表排序
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
上一篇
[Leetcode 146] LRU 缓存
下一篇
[Leetcode 160] 相交链表
