排序链表
难度:
标签:
题目描述
给你链表的头结点 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)
时间复杂度和常数级空间复杂度下,对链表进行排序吗?
代码结果
运行时间: 520 ms, 内存: 29.9 MB
/*
* 思路:
* 1. 将链表转化为流(Stream),然后排序。
* 2. 由于Java的Stream API不支持对链表进行排序,我们将链表转换为ArrayList进行处理。
* 3. 排序完成后,再将ArrayList转回链表。
*/
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
public class Solution {
public ListNode sortList(ListNode head) {
if (head == null) return null;
// 将链表转化为List
List<ListNode> nodeList = new ArrayList<>();
while (head != null) {
nodeList.add(head);
head = head.next;
}
// 使用Stream排序
nodeList = nodeList.stream()
.sorted((n1, n2) -> Integer.compare(n1.val, n2.val))
.collect(Collectors.toList());
// 重新构造链表
ListNode dummy = new ListNode(0);
ListNode current = dummy;
for (ListNode node : nodeList) {
current.next = node;
current = current.next;
}
current.next = null; // 确保最后一个节点的next为null
return dummy.next;
}
}
解释
方法:
该题解使用了自底向上的归并排序算法。首先通过 dummy 节点简化边界情况处理。然后通过 get_length 函数获取链表长度。在归并排序的每一轮中,将链表拆分成若干个长度为 i 的子链表(最后一个子链表长度可能小于 i),并对每对相邻的子链表进行合并。通过 cut 函数将链表切成两半,再通过 merge 函数合并有序链表。不断将 i 乘以 2,对越来越长的有序子链表进行归并,直到整个链表有序。
时间复杂度:
O(n * log n)
空间复杂度:
O(1)
代码细节讲解
🦆
在使用自底向上的归并排序时,为什么选择这种方法而不是递归的自顶向下归并排序?
▷🦆
函数 `cut` 在遇到子链表长度不足指定长度时是如何处理的,它是否直接返回了剩余部分作为一个子链表?
▷🦆
对于 `merge` 函数,如何确保合并过程中链表的稳定性,即不改变相同元素的原始顺序?
▷🦆
在每一轮归并结束时,如何处理尾部可能悬空的子链表?
▷相关问题
合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = [] 输出:[]
示例 3:
输入:l1 = [], l2 = [0] 输出:[0]
提示:
- 两个链表的节点数目范围是
[0, 50]
-100 <= Node.val <= 100
l1
和l2
均按 非递减顺序 排列
颜色分类
给定一个包含红色、白色和蓝色、共 n
个元素的数组 nums
,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0
、 1
和 2
分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
示例 1:
输入:nums = [2,0,2,1,1,0] 输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1] 输出:[0,1,2]
提示:
n == nums.length
1 <= n <= 300
nums[i]
为0
、1
或2
进阶:
- 你能想出一个仅使用常数空间的一趟扫描算法吗?
对链表进行插入排序
给定单个链表的头 head
,使用 插入排序 对链表进行排序,并返回 排序后链表的头 。
插入排序 算法的步骤:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
下面是插入排序算法的一个图形示例。部分排序的列表(黑色)最初只包含列表中的第一个元素。每次迭代时,从输入数据中删除一个元素(红色),并就地插入已排序的列表中。
对链表进行插入排序。
示例 1:
输入: head = [4,2,1,3] 输出: [1,2,3,4]
示例 2:
输入: head = [-1,5,3,4,0] 输出: [-1,0,3,4,5]
提示:
- 列表中的节点数在
[1, 5000]
范围内 -5000 <= Node.val <= 5000