JS几种排序姿势

241 阅读3分钟

JS的排序分类

数据结构算法中排序有很多种,常见的、不常见的,至少包含十种以上。根据它们的特性,可以大致分为两种类型:比较类排序和非比较类排序。

  • 比较类排序: 通过比较来决定元素间的相对次序,其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序,在这我们也是主讲比较类排序。大致可以分为下面四大类:

    1. 交换排序

      • 冒泡排序
      • 快速排序
    2. 插入排序

    3. 选择排序

      • 普通选择排序
      • 堆排序
    4. 归并排序

  • 非比较类排序 :不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。大致包含下面几大类:

    1. 桶排序
    2. 基数排序
    3. 计数排序

属性一览

排序算法时间复杂度空间复杂度稳定性
冒泡排序O(n²)O(1)稳定
快速排序O(n*logn)O(n*logn)不稳定
插入排序O(n²)O(1)稳定
选择排序O(n²)O(1)不稳定
堆排序O(n*logn)O(1)不稳定
归并排序O(n*logn)O(N)稳定

代码实现

1. 冒泡排序

冒泡和选择作为学习排序最基础的排序,在这我就直接上代码了

image

  • 代码
// 冒泡排序
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];

function bubbleSort(arr) {
    const len = arr.length;
    // 边界,数组长度小于2
    if (len < 2) {
        return arr;
    }
    for (let i = 0; i < len; i++) {
        for (let j = i + 1; j < len; j++){
            if (arr[i] > arr[j]) {
                let num = arr[i];
                arr[i] = arr[j];
                arr[j] = num;
            }
        }
    }
    return arr;
}

console.log(bubbleSort(a));

2. 选择排序

image

  • 代码
// 选择排序
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];

function selectSort(array) {
    const len = array.length
    let temp
    let minIndex
    for (let i = 0; i < len - 1; i++) {
        minIndex = i
        for (let j = i + 1; j < len; j++) {
            if (array[j] <= array[minIndex]) {
                minIndex = j
            }
        }
        temp = array[i]
        array[i] = array[minIndex]
        array[minIndex] = temp
    }
    return array
}

console.log(selectSort(a));

3. 快速排序 快排前期单独写过文章,里面每一步都有进行讲解5分钟速通快排

  • 思想
    1. 从数列中挑出一个元素,称为 "基准"(pivot);
    2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
    3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

image

  • 代码
// 快排
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];

function quickSort(nums) {
    if (nums.length < 2) {
        return nums;
    }

    let num = nums.splice(0,1);
    let left = [];
    let right = [];
    nums.forEach(element => {
        if (element > num) {
            right.push(element);
        } else {
            left.push(element);
        }
    });
    return quickSort(left).concat(num,quickSort(right));
}

console.log(quickSort(a));

4. 插入排序

  • 思想:
    1. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列
    2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

image

  • 代码
// 插入排序
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];

function insertSort(arr) {
    let fast;
    let slow;
    for (let i = 0; i < arr.length; i++) {
        fast = arr[i];
        slow = i - 1;
        while (slow > 0 && arr[slow] > fast) {
            arr[slow + 1] = arr[slow];
            slow--;
        }
        arr[slow+1] = fast;
    }
    return arr;
}

console.log(insertSort(a));

5. 归并排序

  • 思想
    1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
    2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
    3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
    4. 重复步骤 3 直到某一指针达到序列尾
    5. 将另一序列剩下的所有元素直接复制到合并序列尾 image
// 归并排序
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];

function mergeSort() {
    const merge = (left, right)=>{
        const result = [];
        let l = 0;
        let r = 0;
        while (l < left.length && r < right.length) {
            if (left[l] < right[r]) {
                result.push(left[l++]);
            } else {
                result.push(right[r++])
            }
        }
        while (l < left.length) {
            result.push(left[l++])
        }
        while (r < right.length) {
            result.push(right[r++])
        }
        return result;
    }
    const mergesort = (array) => {
        if (array.length < 2) {
            return array;
        }
        const middle = Math.floor(array.length / 2);
        const left = array.slice(0, middle);
        const right = array.slice(middle, array.length);
        return merge(mergesort(left), mergesort(right));
    }
    return mergesort(a);
}

console.log(mergeSort(a));

6. 堆排序

堆排序个人认为是入门门槛最高的一种算法,理解起来也是有一定难度的,大家可以多花点时间先将图片看一看,理解每一步的原理,然后可以结合代码试试自己手写实现

  • 思想

    1. 创建一个堆 H[0……n-1]
    2. 把堆首(最大值)和堆尾互换
    3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置
    4. 重复步骤 2,直到堆的尺寸为 1 image
  • 代码

// 堆排序
var a = [1, 3, 6, 3, 23, 76, 1, 34, 222, 6, 456, 221];
function heap_sort(arr) {
    var len = arr.length
    var k = 0
    function swap(i, j) {
        var temp = arr[i]
        arr[i] = arr[j]
        arr[j] = temp
    }
    function max_heapify(start, end) {
        var dad = start
        var son = dad * 2 + 1
        if (son >= end) return
        if (son + 1 < end && arr[son] < arr[son + 1]) {
            son++
        }
        if (arr[dad] <= arr[son]) {
            swap(dad, son)
            max_heapify(son, end)
        }
    }
    for (var i = Math.floor(len / 2) - 1; i >= 0; i--) {
        max_heapify(i, len)
    }

    for (var j = len - 1; j > k; j--) {
        swap(0, j)
        max_heapify(0, j)
    }
    return arr
}
heap_sort(a); // [1, 1, 3, 3, 6, 6, 23, 34, 76, 221, 222, 456]

总结

排序算法其实远远不止这几种,,引申开了还有希尔排序等等许许多多,想学习更多在这给大家推荐菜鸟,里面十大经典算法都有实现和图解,我本人也是在上面自学的算法,在这进行一下复习写文章。

我是江河,前端实习生一枚,目前正在准备春招实习,欢迎各位大佬滴滴,文章如有不正之处敬请斧正!