用JS实现各大排序算法

排序算法对于大部分程序员来说都不陌生,很多人第一次在编码的过程中遇到的第一个算法可能就是排序算法。而且很多编程语言内部就内置了排序算法,比如 JSJava 中的 sort

本文会介绍如何怎么用 JS 写我们常见的几种排序算法(冒泡排序、插入排序、选择排序、归并排序、快速排序)

一些概念

在正片开始前,先说明2个概念

  1. 原地排序
    空间复杂度为O(1)的排序算法(即除了数据存储本身的空间不需要额外的辅助空间)。 冒泡、插入、选择都是原地排序的算法。
  2. 算法的稳定性
    待排序的序列中值相等的元素,在经过排序算法排序之后,原有的先后顺序保持不变。

冒泡排序

  • 时间复杂度:最好O(n), 最差O(n^2)
  • 是否原地排序:✅
  • 是否稳定排序:✅
  • 思路:相邻的元素一一比较
js 复制代码
/** 冒泡排序 */
const bubbleSort = (arr) => {
  let swapped = false; // 是否产生过交换
  const length = arr.length;
  for (let i = 0; i < length -  1; i++) {
    for (let j = 0; j < length - i - 1; j++) {
      // 交换
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        swapped = true;
      }
      
    } 
    if (!swapped) {
       return arr;
    }
    swapped = false;
  }
  return arr;
};

插入排序

  • 时间复杂度:最好O(n), 最差O(n^2)
  • 是否原地排序:✅
  • 是否稳定排序:✅
  • 思路:将数据分为已排序区间未排序区间,取未排序区间的元素在已排序区间中找到合适的位置插入,并且保持已排序区间的数据一直有序
js 复制代码
const insertSort = (arr) => {
  const length = arr.length;
  let target;
  for(let i = 0 ;i < length; i++) {
    target = arr[i];
    let j = i - 1;
    for(; j >=0 ;j--) {
      if (arr[j] > target) {
        arr[j + 1] = arr[j] // 移动数据
      } else {
        break;
      }
    }
    arr[j + 1] = target // 插入数据
  }

  return arr;
};

选择排序

  • 时间复杂度:最好最差都是O(n^2)
  • 是否原地排序:✅
  • 是否稳定排序:❌
  • 思路:将数据分为已排序区间未排序区间,取未排序区间的元素中最小的元素,把它放在已排序区间的末尾
js 复制代码
const selectSort = (arr) => {
  let min;
  const length = arr.length;
  for (let i = 0; i < length - 1; i++) {
    min = i;
    for (let j = i + 1; j < length; j++) {
      if(arr[j] < arr[min]) {
        min = j;
      }
    }
    [arr[min], arr[i]] = [arr[i], arr[min]];
  }

  return arr;
};

归并排序

  • 时间复杂度:O(nlogn)
  • 空间复杂度 O(n)
  • 是否原地排序:❌
  • 是否稳定排序:✅
  • 思路:分治思想,把数组从中间分成前后两部分,然后对前后两部分分别排序,再将排好序的两部分合并在一起,这样整个数组就都有序了。(递归)
  • 图解:
js 复制代码
const merge = (leftArray, rightArray) => {
  const mergeArray: number[] = [];
  while (leftArray.length && rightArray.length) {
    if (leftArray[0] < rightArray[0]) {
      mergeArray.push(leftArray.shift());
    } else {
      mergeArray.push(rightArray.shift());
    }
  }
  return mergeArray.concat(leftArray, rightArray);
}


const mergeSort = (arr) => {
  if (arr.length < 2) {
    return arr;
  }
  const mid = Math.floor(arr.length / 2);
  // 将数组分成两部分
  const leftArr = mergeSort(arr.slice(0, mid));
  const rightArr = mergeSort(arr.slice(mid, arr.length));
  // 合并左右数组
  return merge(leftArr, rightArr);
}

快速排序

  • 时间复杂度:O(nlogn)
  • 是否原地排序:✅ (根据实现方式区分)
  • 是否稳定排序:❌
  • 思路:
  1. 先从数组中找一个基准数
  2. 让其他比它大的元素移动到数列一边,比他小的元素移动到数列另一边,从而把数组拆解成两个部分。
  3. 再对左右区间重复第二步,直到各区间只有一个数

这里根据是否原地排序可以分为两种写法
原地快排

js 复制代码
quickSort = (arr) => {
  // 交换的方法
  const swap = (swapArr, a, b) => {
    [swapArr[a], swapArr[b]] = [swapArr[b], swapArr[a]];
  };

  // 分区函数
  const partition = (partArray, left, right) => {
    const pivot = partArray[right];
    let storeIndex = left;
    // 交换元素 保证原地快排
    for(let i = left; i < right; i++) {
      if(partArray[i] < pivot) {
        swap(partArray, i, storeIndex);
        storeIndex++;
      }
    }
    swap(partArray, right, storeIndex);
    return storeIndex;
  };

  const sort = (sortArray, left, right) => {
    if (left > right) {
      return;
    }
    const pivotIndex = partition(sortArray, left, right);
    sort(sortArray, left, pivotIndex - 1);
    sort(sortArray, pivotIndex + 1, right);
  }

  sort(arr, 0, arr.length - 1);
  return arr;
};

非原地快排

js 复制代码
const quickSort = (arr) => {
  if(arr.length < 2) {
    return arr
  }
  const pivotIndex = Math.floor(arr.length / 2)
  const pivot = arr.splice(pivotIndex, 1)[0] // 找到分区点
  const leftArray = [], rightArray = []
  for(let i = 0; i < arr.length; i++) {
    if(arr[i] < pivot) {
      leftArray.push(arr[i])
    } else {
      rightArray.push(arr[i])
    }
  }
  return [...quickSort(leftArray), pivot, ...quickSort(rightArray)]
}
相关推荐
旧林84317 分钟前
第八章 利用CSS制作导航菜单
前端·css
yngsqq29 分钟前
c#使用高版本8.0步骤
java·前端·c#
Myli_ing1 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风1 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css
hummhumm2 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架