题目:排序链表
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5
答案:
1.归并排序(递归法)
将待排序的序列划分为大小相等(或大致相等)的两个子序列;递归划分直至子序列规模为1;将两个有序的子序列合并为一个有序序列
class Solution {
public ListNode sortList(ListNode head) {
if (head == null || head.next == null)
return head;
//使用快慢指针法,快指针到终点时慢指针指向中点
ListNode fast = head.next, slow = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
//找到右子链表头元素
ListNode tmp = slow.next;
//将中点后续置空,切割为两个子链表
slow.next = null;
//左子链表排序
ListNode left = sortList(head);
//右子链表排序
ListNode right = sortList(tmp);
//合并两段已排好序的链表成一个排序链表
ListNode h = new ListNode(0);
ListNode res = h;
while (left != null && right != null) {
if (left.val < right.val) {
h.next = left;
left = left.next;
} else {
h.next = right;
right = right.next;
}
h = h.next;
}
h.next = left != null ? left : right;
return res.next;
}
}
2.归并排序(自底向上,非递归)
参考链接:https://leetcode-cn.com/problems/sort-list/solution/sort-list-gui-bing-pai-xu-lian-biao-by-jyd/
第一轮合并时intv = 1,即将整个链表切分为多个长度为1的单元,并按顺序两两排序合并,合并完成的已排序单元长度为2。
第二轮合并时intv = 2,即将整个链表切分为多个长度为2的单元,并按顺序两两排序合并,合并完成已排序单元长度为4。
以此类推,直到单元长度intv >= 链表长度,代表已经排序完成。
根据以上推论,我们可以仅根据intv计算每个单元边界,并完成链表的每轮排序合并,例如:
当intv = 1时,将链表第1和第2节点排序合并,第3和第4节点排序合并,……。
当intv = 2时,将链表第1-2和第3-4节点排序合并,第5-6和第7-8节点排序合并,……。
当intv = 4时,将链表第1-4和第5-8节点排序合并,第9-12和第13-16节点排序合并, …
统计链表长度length,用于通过判断intv < length判定是否完成排序;
额外声明一个节点res,作为头部后面接整个链表,用于:intv *= 2即切换到下一轮合并时,可通过res.next找到链表头部h
class Solution {
public ListNode sortList(ListNode head) {
ListNode h, h1, h2, pre, res;
h = head;
int length = 0, intv = 1;
while (h != null) { // 求链表长度
h = h.next;
length++;
}
res = new ListNode(0); // 哨兵节点
res.next = head;
while (intv < length) { //排序截止条件
pre = res;
h = res.next; //链表头部
while (h != null) { //当h == None,代表此轮intv合并完成,跳出
int i = intv;
h1 = h; //单元1的头部
while (i > 0 && h != null) {
h = h.next;
i--;
}
if (i > 0) break;
i = intv;
h2 = h; //单元2的头部
while (i > 0 && h != null) {
h = h.next;
i--;
}
int c1 = intv, c2 = intv - i; //单元1、2的长度
while (c1 > 0 && c2 > 0) { //合并长度为c1, c2的h1, h2链表
if (h1.val < h2.val) {
pre.next = h1;
h1 = h1.next;
c1--;
} else {
pre.next = h2;
h2 = h2.next;
c2--;
}
pre = pre.next;
}
pre.next = c1 == 0 ? h2 : h1;
while (c1 > 0 || c2 > 0) { //pre指向新合并单元尾部
pre = pre.next;
c1--;
c2--;
}
pre.next = h; //新的合并单元的尾部pre指针指向下一个合并单元头部h
}
intv *= 2;
}
return res.next;
}
}