JavaScript中的多种排序算法

桶排序

  • 性能:浪费空间,且只能比较天然数,时间复杂度是 O(m+n)
  • 须要对数据范围在0~n之间的整数进行排序
var testArr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

function skipSort (n, skipArr) {
  let arr = []
  let sortArr = []
  arr.length = n
  for (let i = 0; i <= n; i++) {
    arr[i] = 0
  }
  for (let j = 0; j < skipArr.length; j++) {
    arr[skipArr[j]]++
  }
  for (let t = 0; t <= n; t++) {
    if (arr[t]) {
      for (let k = 1; k <= arr[t]; k++) {
        sortArr.push(t)
      }
    }
  }
  return sortArr
}

console.log('桶排序:', skipSort(10, testArr))
复制代码

冒泡排序

  • 性能:时间复杂度是O(n^2),执行效率低
  • 元素项向上移动至正确的顺序,就好像气泡往上冒同样
  • 从小到大排序,比较两个相邻的项,若是第一个大于第二个则交换他们的位置
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

function bubbleSort (arr) {
  let 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]) {
            let temp = arr[j]
            arr[j] = arr[j + 1]
            arr[j + 1] = temp
        }
    }
  }
  return arr
}
console.log('冒泡排序:',bubbleSort(arr))
复制代码

快速排序

  • chrome使用快速排序的一个变体做为Array.prototype.sort的实现
  • 性能:时间复杂度最差是O(n^2),平均时间复杂度为O(NlogN)
  • 首先,在数组中选择一个中间项做为主元
  • 建立两个指针,左边的指向数组第一个项,右边的指向最后一个项。移动右指针,直到找到一个比主元小的项,接着,移动左边的指针,直到找到一个比主元大的项,而后交换它们。重复这个过程,直到左侧的指针超过了右侧的指针。这个使比主元小的都在左侧,比主元大的都在右侧。这一步叫划分操做
  • 接着,算法对划分后的小数组(较主元小的值组成的的小数组, 以及较主元大的值组成的小数组)重复以前的两个步骤,直到排序完成
  • 切记:必定要先从右往左找,再从左往右找,不然会出错
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

function quickSort (arr, left, right) {
  let len = arr.length
  left = typeof left === 'undefined' ? 0 : left
  right = typeof right === 'undefined' ? len - 1 : right
  if (left > right) {
    return
  }
  let temp = arr[left]
  let i = left
  let j = right
  let t
  while (i !== j) {
    while (arr[j] >= temp && i < j) {
      j--
    }
    while (arr[i] <= temp && i < j) {
      i++
    }
    if (i < j) {
      t = arr[i]
      arr[i] = arr[j]
      arr[j] = t
    }
  }
  arr[left] = arr[i]
  arr[i] = temp
  quickSort(arr, left, i - 1)
  quickSort(arr, i + 1, right)
  return arr
}
console.log('快速排序:',quickSort(arr))
复制代码

选择排序

  • 大概思路是找到最小的放在第一位,找到第二小的放在第二位,以此类推 算法复杂度O(n^2)
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

function selectSort (arr) {
  let len = arr.length
  let minIndex
  let temp
  for (let i = 0; i < len; i++) {
    minIndex = i
    temp = arr[i]
    for (let j = i + 1; j < len; j++) {
        if (arr[j] < arr[minIndex]) { 
            minIndex = j
        }
    }
    arr[i] = arr[minIndex]
    arr[minIndex] = temp
  }
  return arr
}
console.log('选择排序:',selectSort(arr))
复制代码

插入排序

  • 每次排一个数组项,假设数组的第一项已经排序
  • 接着,把第二项与第一项进行对比,第二项是该插入到第一项以前仍是以后
  • 第三项是该插入到第一项以前仍是第一项以后仍是第三项
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

function insertSort (arr) {
  let len = arr.length
  let preIndex
  for (let i = 1; i < len; i++) {
    preIndex = i - 1
    current = arr[i]
    while (preIndex >= 0 && arr[preIndex] > current) {
      arr[preIndex + 1] = arr[preIndex]
      preIndex--
    }
    arr[preIndex + 1] = current
  }
  return arr
}
console.log('插入排序:', insertSort(arr))
复制代码

归并排序

  • Mozilla Firefox使用归并排序做为Array.prototype.sort的实现
  • 归并排序是一种分治算法。本质上就是把一个原始数组切分红较小的数组,直到每一个小数组只有一个位置,接着把小数组归并成较大的数组,在归并过程当中也会完成排序,直到最后只有一个排序完毕的大数组
  • 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可获得原问题的解。
var arr = [3, 5, 3, 5, 9, 7, 6]
console.log('原数组:', arr)

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

function merge (left, right) {
  let result = []
  while (left.length && right.length) {
    if (left[0] < right[0]) {
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }
  result.push(...left)
  result.push(...right)
  return result
}
console.log('归并排序:', mergeSort(arr))
复制代码

堆排序

  • 堆排序其实是利用堆的性质来进行排序的,咱们一般说的堆就是二叉堆,二叉堆又称彻底二叉树或者近似彻底二叉树堆排序是选择排序的一种。能够利用数组的特色快速定位指定索引的元素。数组能够根据索引直接获取元素,时间复杂度为O(1)算法

  • 最大堆的特性以下:chrome

    • 父结点的键值老是大于或者等于任何一个子节点的键值
    • 每一个结点的左子树和右子树都是一个最大堆
  • 最小堆的特性以下:api

    • 父结点的键值老是小于或者等于任何一个子节点的键值
    • 每一个结点的左子树和右子树都是一个最小堆
  • 算法思想数组

    将待排序序列构形成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。而后将剩余n-1个元素从新构形成一个堆,这样会获得n个元素的次小值。如此反复执行,便能获得一个有序序列了。性能

    • 构建初始堆。通常升序采用大顶堆,降序采用小顶堆。从第一个非叶子结点从下至上,从右至左调整结构;
    • 交换堆顶元素和末尾元素,使最大值沉到数组末尾,从新调整堆结构,使其知足定义;
    • 而后继续交换堆顶元素与当前末尾元素,反复执行调整+交换,直到整个序列有序。
const arr = [21, 34, 56, 2, 4, 9, 87]
console.log('原数组:', arr)

let len = arr.length

// 构建最大堆
function buildMaxHeap (arr) {
  let mid = Math.floor(len / 2)
  for (let i = mid; i >= 0; i--) {
    heapify(arr, i)
  }
}

// 子数的调整
function heapify (arr, i) {
  let maxIndex = i
  let left = 2 * i + 1
  let right = 2 * i + 2

  if (left <len && arr[maxIndex] < arr[left]) {
    maxIndex = left
  }

  if (right <len && arr[maxIndex] < arr[right]) {
    maxIndex = right
  }

  if (maxIndex !== i) {
    swap(arr, i, maxIndex)
    heapify(arr, maxIndex)
  }
}

// 交换两值
function swap (arr, i, j) {
  [arr[i], arr[j]] = [arr[j], arr[i]]
}

// 堆排序
function heapSort (arr) {
  buildMaxHeap(arr)

  for (let i = arr.length - 1; i>=0; i--) {
    swap(arr, 0, i)
    len--
    heapify(arr, 0)
  }

  return arr
}

console.log('堆排序:', heapSort(arr))
复制代码

各个排序算法的性能比较

相关文章
相关标签/搜索