Skip to content

排序算法

交换函数

js
function swap(arr, a, b) {
  [arr[b], arr[a]] = [arr[a], arr[b]]
}

冒泡排序

js
function bubbleSort(arr, sort = (a, b) => a - b) {
  const len = arr.length
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        swap(arr, j, j + 1)
      }
    }
  }
}

通过第二个参数,控制正序和逆序

js
// 默认从小到大
function bubbleSort(arr, sort = (a, b) => a - b) {
  const len = arr.length
  for (let i = 0; i < len - 1; i++) {
    for (let j = 0; j < len - 1 - i; j++) {
      if (sort(arr[j], arr[j + 1]) > 0) {
        swap(arr, j, j + 1)
      }
    }
  }
}

bubbleSort(arr, (a, b) => b - a) // 从大到小

选择排序

每一次内循环遍历寻找最小的数,记录下 minIndex,并在这次内循环结束后交换 minIndexi 的位置

js
function selectionSort(arr) {
  const len = arr.length
  for (let i = 0; i < len - 1; i++) {
    let minIndex = i;
    for (let j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    swap(arr, i, minIndex);
  }
}

插入排序

像玩扑克牌一样,抽到一张牌,往手上已排序的牌堆里放

js
function insertionSort(arr) {
  const len = arr.length
  for (let i = 1; i < len; i++) {
    const temp = arr[i];
    let pre = i - 1
    while(arr[pre] > temp) {
      arr[pre + 1] = arr[pre]
      pre--
    }
    arr[pre + 1] = temp
  }
}

希尔排序

js
function shellSort(arr) {
  let len = arr.length
  let gap = Math.floor(len / 2)

  while (gap > 0) {
    // 插入排序
    for (let i = gap; i < len; i++) {
      const temp = arr[i]
      let pre = i - gap
      while (arr[pre] > temp) {
        arr[pre + gap] = arr[pre]
        pre -= gap
      }
      arr[pre + gap] = temp
    }

    gap = Math.floor(gap / 2)
  }
}

归并排序

js
function mergeSort(arr) {
  const len = arr.length
  if (len < 2) return arr
  const min = Math.floor(len / 2)
  const left = arr.slice(0, min)
  const right = arr.slice(min)
  return merge(mergeSort(left), mergeSort(right))
}

function merge(left, right) {
  const res = []
  while (left.length > 0 && right.length > 0) {
    res.push(left[0] <= right[0] ? left.shift() : right.shift())
  }
  return res.concat(left, right)
}

快速排序

  1. 从数列中挑出一个元素,称为 "基准"(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

js
function quickSort(arr, left = 0, right = arr.length - 1) {
  if (left < right) {
    const point = sort(arr, left, right)
    quickSort(arr, left, point - 1)
    quickSort(arr, point + 1, right)
  }
  return arr
}

function sort(arr, left, right) {
  // 以 left 为基准值
  let index = left + 1 // 可交换的位置
  for (let i = index; i <= right; i++) {
    if (arr[i] < arr[left]) {
      swap(arr, i, index) // index 已交换,即此时 index 位置的就是小于基准值的,所以需要右移一位
      index++
    }
  }
  // index - 1 是最后一个小于基准值的,调换 index + 1 和 left
  // 则 left 左边都是小于 left 的,右边都是大于 left 的
  swap(arr, left, index - 1)
  return index - 1 // 把基准值下标返回出去
}