力扣:给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
方法一:
自顶向下的归并排序(递归)(时间复杂度nlogn,空间复杂度logn:空间复杂度来自递归的函数调用栈)
class Solution {
public:
ListNode* sortList(ListNode* head) {
if (!head || !head->next) return head;
// 快慢指针寻找中间节点
ListNode *slow = head, *fast = head->next;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
ListNode* tmp = slow->next; // 中间节点的下一个,作为右子链表
slow->next = nullptr; // 打断链表
ListNode* left = sortList(head); // 前半段头节点是head 递归调用
ListNode* right = sortList(tmp); // 后半段头节点是tmp 递归调用
ListNode* tmpHead = new ListNode(0); // 临时head,循环中不停后移
ListNode* res = tmpHead; // 第一个元素,不参与排序, res->next 是真实的排序后的头节点
while (left && right) { // 两个有序节点合并
if (left->val > right->val) {
tmpHead->next = right;
right = right->next;
}else {
tmpHead->next = left;
left = left->next;
}
tmpHead = tmpHead->next;
}
// 一定有一个链表非空,加入结果链表
tmpHead->next = left? left:right;
// 返回排序后的头节点
return res->next;
}
};
方法二:
自底向上的归并排序(时间复杂度nlogn,空间复杂度O(1))
class Solution {
public:
ListNode* sortList(ListNode* head) {
int n = 0;
for (ListNode* curr = head; curr; curr = curr->next) n++; // 获取链表长度
// 枚举自底向上子链表的长度
for (int i = 1; i < n; i *= 2) {
ListNode* dummy = new ListNode(0), *curr = dummy;
// 当前子链表下需要合并的次数:n / 2*i 向上取整
int cnt = n / 2*i + 1;
while (cnt--) { // 执行合并过程
ListNode *p = head, *q = head; // 首先找到需要合并的两个头节点
for (int j = 0; j < i && q; ++j) q = q->next; // 第二个头节点需要向后走i步
ListNode* next = q; // next记录下次循环的左子链表
for (int j = 0; j < i && next; ++j) next = next->next;
// 合并
int l = 0, r = 0;
while (l < i && r < i && p && q) {
if (p->val < q-> val) curr = curr->next = p, p = p->next, l++;
else curr = curr->next = q, q = q->next, r++;
}
while (l < i && p) curr = curr->next = p, p = p->next, l++;
while (r < i && q) curr = curr->next = q, q = q->next, r++;
head = next;
}
curr->next = nullptr;
head = dummy->next;
}
return head;
}
};