几种排序方法js实现

10种排序算法javaScript实现

时间复杂度和空间复杂度

  • 时间复杂度:执行当前算法消耗的时间
  • 空间复杂度:执行当前算法需要占用多少内存

常见的几种复杂度

  • O(1):常数阶
  • O(n):线性阶
  • O(n²):平方阶
  • O(logn):对数阶
  • O(nlogn):线性对数阶

排序算法分类

  • 冒泡排序
  • 选择排序
    • 普通选择排序
    • 堆排序
  • 插入排序
    • 普通插入排序
    • 希尔排序
  • 快速排序
  • 归并排序
  • 计数排序
  • 基数排序
  • 桶排序

各个排序算法的复杂度

排序名字复杂度和稳定性平均时间复杂度最好最坏空间复杂度稳定性
冒泡排序O(n^2)O(n)O(n^2)O(1)稳定
选择排序O(n^2)O(n^2)O(n^2)O(1)不稳定
堆排序O(n logn)O(n logn)O(n logn)O(1)不稳定
插入排序O(n^2)O(n)O(n^2)O(1)稳定
希尔排序O(n logn)O(n log^2 n)O(n log^2 n)O(1)不稳定
快速排序O(n logn)O(n logn)O(n^2)O(logn)不稳定
归并排序O(n logn)O(n logn)O(n logn)O(n)稳定
计数排序O(n+k)O(n+k)O(n+k)O(k)稳定
桶排序O(n+k)O(n+k)O(n^2)O(n+k)稳定
基数排序O(n*k)O(n*k)O(n*k)O(n+k)稳定

冒泡排序

核心是相邻比较,每次外层循环结束,后半部分的数组都是有序的

// 普通实现
function bubbleSort(arr) {
    let sort = arr;
    let leng = sort.length;
    for (let i = 0; i < leng; i ++) {
        for(let j = 0; j < leng - i - 1; j ++) {
            if (sort[j] > sort[j+1]) swap(sort, j, j+1)
        }
    }
    return sort
}
// 优化
function bubbleSort(arr) {
    let sort = arr
    let leng = sort.length
    while ( leng > 0 ) {
        let hasChange = false
        let position = 0
        // 内层循环不用每次只减一位,只需到上次发生交换的位置就可
        for(let j = 0; j < leng; j ++) {
            if (sort[j] > sort[j+1]) {
                swap(sort, j, j+1)
                position = j
                hasChange = true
            }
        }
        if (!hasChange) break // 如果整个循环都没有发生交换,证明已经有序了
        leng = position
    }
    return sort
}

选择排序

外层循环每次结束,对应数组之前的元素就是有序的,有点像反着的冒泡排序,但原理是不一样的

function selectSort(arr) {
    let sort = arr
    let leng = sort.length
    for (let i = 0; i < leng; i ++) {
        let index = i
        for(let j = i+1; j < leng; j ++) {
            if (sort[j] < sort[index]) {
                index = j
            }
        }
        if (i !== index) swap(sort, i, index)
    }
    return sort
}

堆排序

// 堆排序
function heapSort(arr) {
  var arr_length = arr.length
  if (arr_length <= 1) return arr
  // 1. 建最大堆
  // 遍历一半元素就够了
  // 必须从中点开始向左遍历,这样才能保证把最大的元素移动到根节点
  for (var middle = Math.floor(arr_length / 2); middle >= 0; middle--) maxHeapify(arr, middle, arr_length)
  // 2. 排序,遍历所有元素
  for (var j = arr_length; j >= 1; j--) {
    // 2.1. 把最大的根元素与最后一个元素交换
    swap(arr, 0, j - 1)
    // 2.2. 剩余的元素继续建最大堆
    maxHeapify(arr, 0, j - 2)
  }
  return arr
}
// 建最大堆
function maxHeapify(arr, middle_index, length) {
  // 1. 假设父节点位置的值最大
  var largest_index = middle_index
  // 2. 计算左右节点位置
  var left_index = 2 * middle_index + 1,
    right_index = 2 * middle_index + 2
  // 3. 判断父节点是否最大
  // 如果没有超出数组长度,并且子节点比父节点大,那么修改最大节点的索引
  // 左边更大
  if (left_index <= length && arr[left_index] > arr[largest_index]) largest_index = left_index
  // 右边更大
  if (right_index <= length && arr[right_index] > arr[largest_index]) largest_index = right_index
  // 4. 如果 largest_index 发生了更新,那么交换父子位置,递归计算
  if (largest_index !== middle_index) {
    swap(arr, middle_index, largest_index)
    // 因为这时一个较大的元素提到了前面,一个较小的元素移到了后面
    // 小元素的新位置之后可能还有比它更大的,需要递归
    maxHeapify(arr, largest_index, length)
  }
}

插入排序

// 插入排序
function heapSort(arr) {
  let sort = arr
  let leng = sort.leng
  for (let i = 1; i < leng; i ++) {
    let current = sort[i]
    let last = i - 1
    // 前面的元素更大,并且还没遍历完
    while (arr[last] >= current && last >= 0) {
      // 使用前面的值覆盖当前的值
      arr[last + 1] = arr[last]
      // 向前移动一个位置
      last--
    }
    sort[ordered]

  }
  return arr
}

希尔排序

// 希尔排序
function shellSort(arr) {
  // 外层循环逐步缩小增量 gap 的值
  for (let gap = 5; gap > 0; gap = Math.floor(gap / 2)) {
    // 中层和内层是插入排序
    // 普通插入排序从第1个元素开始,这里分组了,要看每一组的第1个元素
    // 共分成了 gap 组,第一组的第1个元素索引为 gap
    // 第一组元素索引为 0, 0+gap, 0+2*gap,...,第二组元素索引为 1, 1+gap, 2+2*gap,...
    for (let i = gap; i < arr.length; i++) {
      let current_ele = arr[i]
      // 普通插入排序时,j 每次减少1,即与前面的每个元素比较
      // 这里 j 每次减少 gap,只会与当前元素相隔 n*(gap-1) 的元素比较,也就是只会与同组的元素比较
      let ordered_index = i - gap
      while (ordered_index >= 0 && arr[ordered_index] > current_ele) {
        arr[ordered_index + gap] = arr[ordered_index]
        ordered_index -= gap
      }
      arr[ordered_index + gap] = current_ele
    }
  }
  return arr
}

快速排序

// 快速排序
function quickSort(arr) {
    let sort = arr
    let leng = sort.length
    if (leng <= 1) return sort
    let middle = [];
    let left = [];
    let right = [];
    for (let i = 0; i < leng; i ++) {
        if (arr[i] === arr[0]) middle.push(arr[i])
        if (arr[i] < arr[0]) left.push(arr[i])
        if (arr[i] > arr[0]) right.push(arr[i]) 
    }
    return quickSort(left).concat(middle, quickSort(right))
}

归并排序

// 分割
function mergeSort2(arr) {
  // 如果只剩一个元素,分割结束
  if (arr.length < 2) return arr
  // 否则继续分成两部分
  let middle_index = Math.floor(arr.length / 2),
    left = arr.slice(0, middle_index),
    right = arr.slice(middle_index)
  return merge2(mergeSort2(left), mergeSort2(right))
}
// 合并
function merge2(left, right) {
  let result = []
  // 当左右两个数组都还没有取完的时候,比较大小然后合并
  while (left.length && right.length) {
    if (left[0] < right[0]) result.push(left.shift())
    else result.push(right.shift())
  }
  // 其中一个数组空了,另一个还剩下一些元素
  // 因为是已经排序过的,所以直接concat就好了
  // 注意 concat 不改变原数组
  if (left.length) result = result.concat(left)
  if (right.length) result = result.concat(right)
  return result
}

计数排序

function countingSort(array) {
  let count_arr = [], result_arr = []
  // 统计出现次数
  for (let i = 0; i < array.length; i++) {
    count_arr[array[i]] = count_arr[array[i]] ? count_arr[array[i]] + 1 : 1
  }
  // 遍历统计数组,放入结果数组
  for (let i = 0; i < count_arr.length; i++) {
    while (count_arr[i] > 0) {
      result_arr.push(i)
      count_arr[i]--
    }
  }
  return result_arr
}

参考:https://juejin.cn/post/6844903814340804615

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值