基础必备之十大经典排序

辅助函数

//数组交换位置
export const swap = (arr, i, j) => {
  var temp = arr[i]
  arr[i] = arr[j]
  arr[j] = temp
}
//生成随机数
export const randomNum = (n, num) => {
  const arr = [],
    t = num || n
  for (let i = 0; i < n; i++) {
    arr.push(Math.floor(Math.random() * t))
  }
  return arr
}
//Time test
export const timeTest = (fn, arr, ...values) => {
  console.time(`${fn.name}_${arr.length}`)
  const result = fn(arr, ...values)
  console.timeEnd(`${fn.name}_${arr.length}`)
  return result
}

数组sort

export const sort = (arr) => {
  return arr.sort((a, b) => a - b)
}

console结果可能不准确,按F12打开控制台查看

冒泡排序

export const bubbleSort = (arr) => {
  let low = 0,
    high = arr.length - 1,
    j
  while (low < high) {
    for (j = low; j < high; ++j) {
      //正向冒泡,找到最大者
      if (arr[j] > arr[j + 1]) swap(arr, j, j + 1)
    }
    --high //修改high值, 前移一位
    for (j = high; j > low; --j) {
      //反向冒泡,找到最小者
      if (arr[j] < arr[j - 1]) swap(arr, j, j - 1)
    }
    ++low //修改low值,后移一位
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

选择排序

export const selectSort = (arr) => {
  let minIndex
  const len = arr.length
  for (let i = 0; i < len; i++) {
    minIndex = i
    for (let j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) minIndex = j //排出最小数index
    }
    swap(arr, i, minIndex)
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

插入排序

export const insertSort = (arr, l = 0, r = arr.length - 1) => {
  for (let i = 1 + l; i <= r; i++) {
    const temp = arr[i]
    let j = i
    while (j > 0 + l && temp < arr[j - 1]) {
      //如果后一个比前一个小,前面的整体后移,跳出循环后插入
      arr[j] = arr[j - 1]
      j--
    }
    if (j != i) arr[j] = temp
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

希尔排序

  • 希尔排序即是插入排序的优化,按一定增量分组后进行插入排序,增量自减再排序,直到排序完成
export const shellSort = (arr) => {
  let temp,
    gap = 1
  const len = arr.length
  // 求出起始增量
  while (gap < len) {
    gap = gap * 3 + 1
  }
  for (gap; gap > 0; gap = Math.floor(gap / 3)) {  // gap为1即排序完成
    for (let i = gap; i < len; i++) { // 据增量分组
      temp = arr[i]
      let j = i - gap 
      //如果后一个比前一个小,前面的整体后移,跳出循环后插入
      for (j; j >= 0 && arr[j] > temp; j -= gap) {
        arr[j + gap] = arr[j]
      }
      arr[j + gap] = temp
    }
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

归并排序

  • 分治思想,合二为一
//归并排序  自顶向下  (原地排序)
export const mergeSort = (arr, l = 0, r = arr.length - 1) => {
  if (r <= l) return //元素一个自身有序,不需要merge
  let mid = ~~((l + r) / 2) // 取中间元素坐标
  mergeSort(arr, l, mid)
  mergeSort(arr, mid + 1, r)
  if (arr[mid] >= arr[mid + 1]) { //对已经有序的两边作merge操作 [1,4,5(mid)  ,2,3,6]
    merge(arr, l, mid, r)
  }
  return arr
}
export const merge = (arr, left, mid, right) => {
  // 原地排序,需要额外数组来存值
  const result = []
  for (let i = 0; i <= right - left; i++) {
    result[i] = arr[i + left]
  }
  // arr -> [left,...mid,...right]
  //         index 
  // result   1,4,   5    2,3,6
  //          l      mid  r   
  // 比较l,r值,替换index,替换过后l++或者r++,然后index++,考虑l和r边界情况
  let index = left,
    l = left,
    r = mid + 1
  while (index <= right) {
    if (l > mid) {
      arr[index] = result[r - left]
      r++
    } else if (r > right) {
      arr[index] = result[l - left]
      l++
    } else if (result[l - left] < result[r - left]) {
      arr[index] = result[l - left]
      l++
    } else {
      arr[index] = result[r - left]
      r++
    }
    index++
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

单路/双路快排

  • 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
const quickSort = (arr, l = 0, r = arr.length - 1, way = 1) => {
  if (r <= l) return
  const partitionIndex = way === 1 ? partition1(arr, l, r) : partition2(arr, l, r)
  quickSort(arr, l, partitionIndex - 1, way)
  quickSort(arr, partitionIndex + 1, r, way)
  return arr
}
export const quickSort1 = arr => quickSort(arr)
export const quickSort2 = arr => {
  return quickSort(arr, 0, arr.length - 1, 2)
}

partition1

const partition1 = (arr, l, r) => {
  const randomIndex = ~~(Math.random() * (r - l) + l)
  swap(arr, l, randomIndex)
  // [3,0,2,3]
  const v = arr[l] // 1
  let j = l,
    i = l + 1
  for (i; i <= r; i++) {
    if (arr[i] < v ) {
      swap(arr, ++j, i)
    }
  }
  swap(arr, l, j)
  return j
}

partition2

const partition2 = (arr, l, r) => {
  const randomIndex = Math.ceil(Math.random() * (r - l) + l)
  swap(arr, l, randomIndex)
  const v = arr[l]
  let i = l + 1,
    j = r
  while (true) {
    while (i <= r && arr[i] < v) i++
    while (j >= l + 1 && arr[j] > v) j--
    if (i > j) break
    swap(arr, i, j)
    i++
    j--
  }
  swap(arr, l, j)
  return j
}
quickSort1

console结果可能不准确,按F12打开控制台查看

三路快排

export const quickSort3 = (arr, l = 0, r = arr.length - 1) => {
  // 数量少的时候插入排序性能优
  if (r - l <= 15) {
    return insertSort(arr, l, r)
  }
  const randomIndex = ~~(Math.random() * (r - l) + l)
  swap(arr, l, randomIndex)
  const v = arr[l]
  let lt = l, //arr[l+1,...lt]
    gt = r + 1, // arr[gt,...r]
    i = l + 1 // arr[lt+1,...i]
  while (i < gt) {
    if (arr[i] < v) {
      swap(arr, i, ++lt)
      i++
    } else if (arr[i] > v) {
      swap(arr, i, --gt)
    } else {
      i++
    }
  }
  swap(arr, l, lt)
  quickSort3(arr, l, lt)
  quickSort3(arr, gt, r)
  return arr
}

console结果可能不准确,按F12打开控制台查看

堆排序

export const heapSort = (arr) => {
  const len = arr.length
  buildMaxHeap(arr) // 建立大顶推
  for (let i = len - 1; i > 0; i--) {
    swap(arr, 0, i) //  逆序排,取堆max节点
    heapify(arr, 0, i - 1) // 重新调整堆结构
  }
  return arr
}
//建立大顶堆
const buildMaxHeap = (arr) => {
  for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
    heapify(arr, i)
  }
}
//堆调整
const heapify = (arr, i, len = arr.length) => {
  let l = 2 * i + 1,
    r = 2 * i + 2,
    max = i
  if (l < len && arr[l] > arr[max]) {
    max = l
  }
  if (r < len && arr[r] > arr[max]) {
    max = r
  }
  if (max != i) {
    swap(arr, i, max)
    heapify(arr, max, len)
  }
}

console结果可能不准确,按F12打开控制台查看

计数排序

export const countSort = (arr) => {
  let maxValue = arr[0],
    i = 1
  while (i < arr.length) {
    if (maxValue < arr[i]) {
      maxValue = arr[i]
    }
    i++
  }
  // 根据最大数新建数组,原数组的值作为下标,个数作为值
  const bucket = new Array(maxValue + 1)
  let sortIndex = 0
  for (let j = 0; j < arr.length; j++) {
    if (!bucket[arr[j]]) {
      bucket[arr[j]] = 0
    }
    bucket[arr[j]]++ //计数
  }
  for (let n = 0; n < bucket.length; n++) {
    while (bucket[n] > 0) {
      //取出排序
      arr[sortIndex++] = n
      bucket[n]--
    }
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看

桶排序

//桶排序  适用于近乎有序的数组
export const bucketSort = (arr, l = 0, r = arr.length - 1, bucketSize = 15) => {
  if (r - l <= 15) return insertSort(arr, l, r)
  let min = arr[l],
    max = arr[l + 1]
  for (let i = l; i <= r; i++) {
    if (arr[i] < min) {
      min = arr[i]
    } else if (arr[i] > max) {
      max = arr[i]
    }
  }
  //初始化桶
  const bucketCount = ~~((max - min) / bucketSize) + 1, //桶的个数
    buckets = []
  let m = 0
  for (m; m < bucketCount; m++) {
    buckets[m] = []
    buckets[m].insert = function(node){
      const len = this.length
      let i = 0
      if(len === 0) this[i] = node
      while(i < len) {
        if(node <= this[i]) {
          this.unshift(node)
          break
        }
        if(this[i] < node && !this[i+1]) {
          i++
          this.splice(i,0,node)
          break
        } else if(this[i] <= node && node <= this[i + 1]) {
          i++
          this.splice(i,0,node)
          break
        }
        i++
      }
    }
  }
  //根据映射分配
  for (l; l <= r; l++) {
    m = ~~((arr[l] - min) / bucketSize)
    buckets[m].insert(arr[l])
  }
  // 对每个桶再进行桶排序
  // let index = 0
  arr = buckets.reduce((result, item) => {
    result.push(...item)
    return result
  }, [])
  return arr
}

console结果可能不准确,按F12打开控制台查看

基数排序

//LSD 基数排序
export const radixSort(arr) => {
  let maxNum = arr[0],
    mod = 10,
    dev = 1
  arr.forEach(v => {
    if (v > maxNum) maxNum = v
  })
  const maxDight = String(maxNum).length
  for (let i = 0; i < maxDight; i++, mod *= 10, dev *= 10) {
    const buckets = []
    for (let j = 0; j < arr.length; j++) {
      const index = ~~((arr[j] % mod) / dev)
      if (!Array.isArray(buckets[index])) buckets[index] = []
      buckets[index].push(arr[j])
    }
    arr = buckets.reduce((r, v) => [...r, ...v], [])
  }
  return arr
}

console结果可能不准确,按F12打开控制台查看