148. 排序链表 归并排序 | 快速排序
148. 排序链表
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
输入: 4->2->1->3
输出: 1->2->3->4
来源:https://leetcode-cn.com/problems/sort-list/
归并排序法:
- 利用快慢指针将链表分为前后半段
- 先对
slow.next
找到后半段并进行排序 - 断开链表前后半段,对前半段进行排序
- 合并排序完成后的左右两条链表
function mergeTwoLists(x, y) {
if (!x || !y)
return x ? x : y;
let p = new ListNode(-1);
let root = p;
while (x && y) {
if (x.val < y.val) {
p.next = x;
x = x.next;
} else {
p.next = y;
y = y.next;
}
p = p.next;
}
/* 连接剩余部分 */
p.next = x ? x : y;
return root.next;
}
function mergeSort(root) {
/* 数量不到2个直接返回 */
if (!root || !root.next) return root;
/* 快慢指针找出中间点 */
let slow = root, fast = root.next.next;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
}
/* 对右半部分进行归并 */
let right = mergeSort(slow.next);
/* 断开左右连接 */
slow.next = null;
/* 左半部分归并 */
let left = mergeSort(root);
/* 合并左右 */
return mergeTwoLists(left, right);
}
var sortList = function (head) {
return mergeSort(head);
};
快速排序法:
- 设置头尾指针,将第一个结点的值作为参考值,
- 遍历后面的元素,将小于参考值的节点抽离到临时链表
- 此时原链表全为大于等于参考值的元素,且第一个值为参考值,临时链表全为小于参考值的元素,将原链表接到临时链表后面即将参考节点放到了最终的位置
- 覆盖原链表,对参考值左边和右边元素进行相同的操作
/* 对(st,ed)区间的元素进行排序 */
function quickSort(st, ed) {
/* 至少存在一个值 */
if (st == ed || st.next == ed) return st;
/* x为遍历指针 p为参考值 */
let x = st.next, p = st.next;
/* 临时链表存小于p的所有元素 */
let tpSt = new ListNode(-1);
let tp = tpSt;
/* 遍历(st,ed)区间 */
while (x.next != ed) {
/* 当前值小于p则抽离到临时链表 */
if (x.next.val < p.val) {
tp.next = x.next;
tp = tp.next;
/* 原链表删除 */
x.next = x.next.next;
} else { x = x.next; }
}
/* 原链表全大于p 临时链表全小于p*/
tp.next = st.next;
st.next = tpSt.next;
quickSort(st, p);
quickSort(p, ed);
return st.next;
}
var sortList = function (head) {
if (!head || !head.next)
return head;
let newHead = new ListNode(-1);
newHead.next = head;
return quickSort(newHead, null);
};