148. 排序链表

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

示例 1:

img

1
2
输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:

img

1
2
输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

1
2
输入:head = []
输出:[]

题目大意

给定链表的头节点 head,要求将链表按升序排列并返回排序后的链表头节点。

解题思路

对于链表排序,最适合的高效算法是归并排序,原因如下:

  1. 归并排序的时间复杂度为 O (n log n),是链表排序的最优选择
  2. 链表的归并操作不需要像数组那样额外分配 O (n) 的空间
  3. 链表的中点查找可以通过快慢指针高效实现

归并排序的核心步骤:

  1. 分解:使快慢指针找到链表中点,将链表分成两部分
  2. 递归:对左右两部分分别进行排序
  3. 合并:将两个已排序的链表合并成一个有序链表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Solution {
public:
ListNode* sortList(ListNode* head) {
// 边界条件:空链表或只有一个节点,直接返回
if (head == nullptr || head->next == nullptr) {
return head;
}

// 1. 找到链表中点,将链表分成两部分
ListNode* mid = findMiddle(head);
ListNode* rightHead = mid->next;
mid->next = nullptr; // 切断链表

// 2. 递归排序左右两部分
ListNode* left = sortList(head);
ListNode* right = sortList(rightHead);

// 3. 合并两个已排序的链表
return merge(left, right);
}

private:
// 找到链表的中点(使用快慢指针)
ListNode* findMiddle(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head;
}

ListNode* slow = head;
ListNode* fast = head->next; // 快指针超前一步,确保中点在左半部分

while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}

return slow;
}

// 合并两个已排序的链表
ListNode* merge(ListNode* l1, ListNode* l2) {
// 创建虚拟头节点,简化合并操作
ListNode* dummy = new ListNode(0);
ListNode* curr = dummy;

// 合并两个链表
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
curr->next = l1;
l1 = l1->next;
} else {
curr->next = l2;
l2 = l2->next;
}
curr = curr->next;
}

// 处理剩余节点
curr->next = (l1 != nullptr) ? l1 : l2;

// 保存结果并释放虚拟节点
ListNode* result = dummy->next;
delete dummy;
return result;
}
};