排序算法

198 阅读3分钟

时间复杂度

  • O(1):无论多少数据,都不会影响操作时间。例如读取对象属性。
  • O(n):随着数据的增加复杂度线性增长。比如获取一堆数中最大的的值,需要遍历一边所有数据,数据越多速度越慢。
  • O(n²):例如排序算法中的冒泡,把上面的O(n)行为重复n次。找到最大数,取出。在剩下那堆里继续找最大数。
  • O(logn):二分法,从排好序的数中找某一特定数。

交换函数

    const swap = (nums, i, j) => {
      let temp = nums[i];
      nums[i] = nums[j];
      nums[j] = temp;
      return nums;
    };

选择排序

描述

  • 选择所有数中最小的排到第一位
  • 从第二位开始选择剩余最小排到第二位,重复

冒泡排序

描述

  • 从第一个数开始比较相邻的两个数的大小
  • 重复交换直到最后一个数
  • 此时最后一个数即为最值,下次交换区间为第一个数至倒数第二个数
  • 时间O(n²),空间O(1)
  • 通过添加一个标识,如果本次未进行任何交换,那这个数组已经排好序了。直接返回结果

code

    const bubbleSort = nums => {
      if (nums.length <= 1) return nums;
      let flag = false;
      for (let i = 0; i < nums.length; i++) {
        for (let j = 0; j < nums.length - i - 1; j++) {
          if (nums[j] > nums[j + 1]) {
            swap(nums, j, j + 1);
            flag = true;
          }
        }
        if (!flag) {
          return nums;
        } else {
          flag = false;
        }
      }
      return nums;
    };

插入排序

描述

  • 默认第一位数是一个已排好序的数组A
  • 将第二位数,由数组A从后向前比较,将其插入在遇到第一个比他小的数后面

code

    const insertSort = nums => {
      if (nums.length <= 1) return nums;

      let len = nums.length;
      let curr, pre;
      for (let i = 1; i < len; i++) {
        pre = i - 1;
        curr = nums[i];
        while (pre >= 0 && nums[pre] > curr) {
          nums[pre + 1] = nums[pre];
          pre--;
        }
        nums[pre + 1] = curr;
      }
      return nums;
    };

希尔算法

描述

  • 希尔算法是插入算法的一种优化
  • 设置一个增量gap,例如[3,9,7,5],gap可以首先设置为2。将数组分为[3,7]和[5,9]进行排序。
  • 第一次排序后数组为[3,5,7,9],然后把增量再除2设置为1。
  • 当增量为1时作为一个排序来处理。
  • 希尔排序的核心在增量的设置。

code

    const shellSort = nums => {
      let len = nums.length;
      // 每次间隙都对半减小
      for (let gap = Math.floor(len / 2); gap > 0; gap = Math.floor(gap / 2)) {
        for (let i = gap; i < len; i++) {
          let j = i;
          let curr = nums[i];
          while (j - gap >= 0 && curr < nums[j - gap]) {
            nums[j] = nums[j - gap];
            j = j - gap;
          }
          nums[j] = curr;
        }
      }
      return nums;
    };

归并排序

描述

  • 一个有序序列可以由两个有序子序列组成。先将一个序列分为两个子序列,进行归并排序。
  • 将问题转化为对两个已排序子序列进行排序。
  • 递归结束条件为子序列长度为1
  • merge时需要开辟新空间

code

    const mergeSort = nums => {
      if (nums.length <= 1) return nums;
      let m = Math.floor(nums.length / 2);
      let l = nums.slice(0, m);
      let r = nums.slice(m);
      return merge(mergeSort(l), mergeSort(r));
    };

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

快排

描述

  • 选择一个基准点,把数列中小于基准点的放在左边,大于的放在右边
  • 递归上述过程直到子序列长度为1

code

    const quickSort = nums => {
      if (nums.length <= 1) return nums;
      let pivotIndex = Math.floor(nums.length / 2);
      let pivot = nums.splice(pivotIndex, 1)[0];
      let l = [];
      let r = [];
      for (let i = 0; i < nums.length; i++) {
        nums[i] < pivot ? l.push(nums[i]) : r.push(nums[i]);
      }
      return quickSort(l).concat([pivot], quickSort(r));
    };