148. 排序链表

114 阅读2分钟

题目描述

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

解题思路

算法

归并排序

思路

不断地将链表拆成两部分,对这两部分分别排序,然后合成一个新链表

过程

题目给的是链表头节点 head,拆分链表需要知道头节点 head,以及链表的长度 n

首先在归并排序之前,计算一次链表的总长度 n,然后开始归并排序

我们排序的过程,首先对左区间和右区间排序,然后对着两部分进行合并,因此先要找左区间和右区间,上面已经说到拆分需要知道两个东西,一个是头节点,一个是长度

我们首先计算中点 mid = n >> 1,设左区间起始位置为 p1,右为 p2

对于两个区间:

左区间:头节点为 head,长度为 mid,因此 p1 在一开始直接赋值为 head 右区间:头节点我们需要计算,在下面讲解,长度为 n - mid

在求 p2 的时候,我们还需要将 p1, p2 断开,这点需要注意

那么我们首先设置 p2head,让他移动到右区间起始节点的前一位,这是为了断开操作,那么比如 n = 4mid = 2p2 需要移动 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
}