题目描述
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
解题思路
算法
归并排序
思路
不断地将链表拆成两部分,对这两部分分别排序,然后合成一个新链表
过程
题目给的是链表头节点 head,拆分链表需要知道头节点 head,以及链表的长度 n
首先在归并排序之前,计算一次链表的总长度 n,然后开始归并排序
我们排序的过程,首先对左区间和右区间排序,然后对着两部分进行合并,因此先要找左区间和右区间,上面已经说到拆分需要知道两个东西,一个是头节点,一个是长度
我们首先计算中点 mid = n >> 1,设左区间起始位置为 p1,右为 p2
对于两个区间:
左区间:头节点为 head,长度为 mid,因此 p1 在一开始直接赋值为 head
右区间:头节点我们需要计算,在下面讲解,长度为 n - mid
在求 p2 的时候,我们还需要将 p1, p2 断开,这点需要注意
那么我们首先设置 p2 为 head,让他移动到右区间起始节点的前一位,这是为了断开操作,那么比如 n = 4,mid = 2,p2 需要移动 1 位,因此如果用 for:
for (let i = 1; i < mid; i++) {
p2 = p2.next
}
然后做断开:用 tmp 缓存 p2.next,断开后再将 p2 赋值为 tmp
这之后,我们通过递归来完成左,右两个区间(链表)的排序
const l1 = mergeSort(p1, mid)
const l2 = mergeSort(p2, n - mid)
返回的 l1, l2 是排序完毕的两个链表,我们需要将它合并,这里和寻常的归并排序实际上是一样的
最终,我们需要在归并排序的函数返回合并后链表的头节点
代码
// partition
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function (head) {
const arr = []
let cur = head
while (cur) {
arr.push(cur.val)
cur = cur.next
}
cur = head
quickSort(arr, 0, arr.length - 1)
while (cur) {
cur.val = arr.shift()
cur = cur.next
}
return head
}
function quickSort(arr, l, r) {
partition(arr, l, r)
insertSort(arr, l, r)
}
function partition(arr, l, r) {
while (r - l > 16) {
let x = l,
y = r,
base = getMid(arr[l], arr[r], arr[Math.floor((r + l) / 2)])
do {
while (x < y && arr[x] < base) x++
while (x < y && arr[y] > base) y--
if (x < y) {
;[arr[x], arr[y]] = [arr[y], arr[x]]
x++
y--
}
} while (x < y)
partition(arr, x + 1, r)
r = x
}
}
function getMid(a, b, c) {
if (a > b) return getMid(b, a, c)
if (a > c) return getMid(c, b, a)
if (b > c) return getMid(a, c, b)
return b
}
function insertSort(arr, l, r) {
for (let i = l + 1; i <= r; i++) {
let j = i
while (arr[j] < arr[j - 1]) {
;[arr[j - 1], arr[j]] = [arr[j], arr[j - 1]]
j--
}
}
}
// merge sort solution
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function (head) {
const arr = []
let cur = head
while (cur) {
arr.push(cur.val)
cur = cur.next
}
mergeSort(arr, 0, arr.length - 1)
const ret = new ListNode()
let p = ret
for (const x of arr) {
p.next = new ListNode(x, null)
p = p.next
}
return ret.next
}
function mergeSort(arr, l, r) {
if (l >= r) return
const mid = (l + r) >> 1,
temp = []
let p1 = l,
p2 = mid + 1,
k = 0
mergeSort(arr, l, mid)
mergeSort(arr, mid + 1, r)
while (p1 <= mid || p2 <= r) {
if (p2 > r || (p1 <= mid && arr[p1] <= arr[p2])) {
temp[k++] = arr[p1++]
} else {
temp[k++] = arr[p2++]
}
}
for (let i = l; i <= r; i++) {
arr[i] = temp[i - l]
}
}
//merge sort
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var sortList = function (head) {
if (!head || !head.next) return head
let n = 0
let cur = head
while (cur) {
cur = cur.next
n++
}
return mergeSort(head, n)
}
function mergeSort(head, n) {
if (n <= 1) return head
const mid = n >> 1,
ret = new ListNode()
let p = ret,
p1 = head,
p2 = head
for (let i = 1; i < mid; i++) {
p2 = p2.next
}
const tmp = p2.next
p2.next = null
p2 = tmp
let l1 = mergeSort(head, mid)
let l2 = mergeSort(p2, n - mid)
while (l1 || l2) {
if (!l2 || (l1 && l1.val <= l2.val)) {
p.next = l1
l1 = l1.next
} else {
p.next = l2
l2 = l2.next
}
p = p.next
}
return ret.next
}