总结一下几种算法排序(Javascript)

181 阅读2分钟

下面主要是总结下我遇到的一些排序算法,其实我们都知道js中有sort方法,这篇文章只是针对那些对算法有兴趣的人准备的。 为了方便实现,我们先定义几个常规函数

const Compare = {
    LESS_THAN: -1,
    BIGGER_THAN: 1,
    EQUAL: 0,
}

function defaultCompare(a, b) {
    return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN;
}

function swap(array, a, b) {
    const temp = array[a];
    array[a] = array[b];
    array[b] = temp;
}

对于swap也可以使用ES6来实现

function swap(array, a, b) {
    [array[a], array[b]] = [array[b], array[a]];  
}

冒泡排序

const test_arr0 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]  // 用来测试的数组
function bubbleSort(array, compareFn = defaultCompare) {
    const { length } = array;
    for (let i = 0; i < length; i++) {
        for (let j = 0; j < length - 1 - i; j++) {
            if (compareFn(array[j], array[j + 1]) === Compare.BIGGER_THAN) {
                swap(array, j, j + 1);
            }
        }
    }
    return array;
}

const bubble_res = bubbleSort(test_arr0);
console.log('冒泡排序:', bubble_res);

选择排序

const test_arr1 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]  // 用来测试的数组
function selectionSort(array, compareFn = defaultCompare) {
    const { length } = array;
    let indexMin;  // 定义最小的哪个值的index
    for (let i = 0; i < length - 1; i++) {  // 因为我们要对比所以我们只用轮询次数少一
        indexMin = i; // 首先定义的是最小的是当前的i
        // 下面的内容就是找到最小的值
        for (let j = i; j < length; j++) {
            if (compareFn(array[indexMin], array[j]) === Compare.BIGGER_THAN) {
                indexMin = j; // 如果当前最小的indexMin不是最小的,那么我们就设置最小的indexMin 是j
            }
        }
        // 如果当前的值不是最小的,我们就交换位置
        if (i !== indexMin) {
            swap(array, i, indexMin);
        }
    }
    return array;
}

const selection_res = selectionSort(test_arr1);
console.log('选择排序:', selection_res);

插入排序

const test_arr2 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]  // 用来测试的数组
function insertionSort(array, compareFn = defaultCompare) {
    const { length } = array;
    let temp; // 存储要插入的元素
    // 从第二个开始
    for (let i = 1; i < length; i++) {
        let j = i; // 我们存储i
        temp = array[i]; // 当前的值
        // 边界条件是j>0以及我们当前的值比它前边的值小
        while (j > 0 && compareFn(array[j - 1], temp) === Compare.BIGGER_THAN) {
            array[j] = array[j - 1]; // 将前边的值向右移动,移动到当前的值。也就是说当前的值等于前边的值
            j--; // 位置变为当前位置的前一个位置
        }
        array[j] = temp; // 插入到当前的位置
    }
    return array;
}

const insertion_res = insertionSort(test_arr2);
console.log('插入排序:', insertion_res);

归并排序

const test_arr3 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28];  // 用来测试的数组
function mergeSort(array, compareFn = defaultCompare) {
    if (array.length > 1) { // 如果数组的长度大于1
        const { length } = array;
        const middle = Math.floor(length / 2); // 我们这个向下取整
        const left = mergeSort(array.slice(0, middle), compareFn);
        const right = mergeSort(array.slice(middle, length), compareFn);
        array = merge(left, right, compareFn);
    }
    return array;
}

function merge(left, right, compareFn) {
    let i = 0;
    let j = 0;
    const result = [];
    while (i < left.length && j < right.length) {
        result.push(
            compareFn(left[i], right[j]) === Compare.LESS_THAN ? left[i++] : right[j++]
        )
    }
    return result.concat(i < left.length ? left.slice(i) : right.slice(j));
}

const merge_res = mergeSort(test_arr3);
console.log('归并排序:', merge_res);

快速排序

快速排序的最坏运行情况是O(n²),比如说顺序数列的快排。但它的平摊期望时间是O(n log n) ,且O(n log n)记号中隐含的常数因子很小, 比复杂度稳定等于O(n log n)的归并排序要小很多。所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

const test_arr4 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]  // 用来测试的数组

function quickSort(array, left, right, compareFn = defaultCompare) {
    var { length } = array; // 获取数组长度
    var partitionIndex; // 获取当前操作的index
    var left = typeof left != "number" ? 0 : left; // 最左边
    var right = typeof right != "number" ? length - 1 : right; // 最右边

    // 最左边和最右边对比 
    if (compareFn(left, right) === Compare.LESS_THAN) {
        partitionIndex = partition(array, left, right);
        quickSort(array, left, partitionIndex - 1); // 比较基准值左边部分
        quickSort(array, partitionIndex + 1, right); // 比较基准值右边的部分
    }
    return array;
}

function partition(array, left, right, compareFn = defaultCompare) { // 分区操作
    var pivot = left; // 设置最左边的值是轴心点(基准值)
    var index = left + 1; // 从右边起第一个值开始比较
    // 开始循环遍历,从右边起第一个值开始,一直到最右边
    for (var i = index; i <= right; i++) {
        // 如果当前的值比起基准值小,那么我们就将这些值都放到基准值右边
        if (compareFn(array[i], array[pivot]) === Compare.LESS_THAN) {
            swap(array, i, index); // 起始位置和当前的i交换
            index++; // 
        }
    }
    // 此刻,因为基准值右边的值都比他小,所以我们将基准值和当前比他小的值的序列最右边的值交换,此刻,也就是说基准值左边的值都是比他小的
    swap(array, pivot, index - 1);
    return index - 1; // 返回的值是比当前基准值小的数组的最右边的项
}
const quick_res = quickSort(test_arr4);
console.log('快速排序:', quick_res);

希尔排序

function shellSort(array, compareFn = defaultCompare) {
    let { length } = array;
    let temp;
    let gap = 1;

    while (gap < length / 3) {  // 动态定义间隔序列
        gap = gap * 3 + 1;
    }
    for (gap; gap > 0; gap = Math.floor(gap / 3)) {
        for (var i = gap; i < length; i++) {
            temp = array[i];
            for (var j = i - gap; j >= 0 && array[j] > temp; j -= gap) {
                array[j + gap] = array[j];
            }
            array[j + gap] = temp;
        }
    }
    return array;
}

const test_arr5 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28]  // 用来测试的数组
const shell_res = shellSort(test_arr5);
console.log('希尔排序:', shell_res);

计数排序(注意这个只是适合整数排序)

function countingSort(array) {
    if (array.length < 2) {
        return array;
    }
    const maxValue = findMaxValue(array);

    const counts = new Array(maxValue + 1); // 这里创建了一个长度为当前数组中最大值的长度的数组

    array.forEach(element => {
        // console.log(typeof counts); // object
        if (!counts[element]) {
            counts[element] = 0;
        }
        counts[element]++;
    });
    // console.log('第一步遍历之后的counts', counts); // 结果就是得到了 counts[1:1,2:1,28:1....];其实是一个对象

    let sortedIndex = 0;
    counts.forEach((count, i) => {
        // console.log(count, i); // 1 1    1  2  1 28   1 36 2 45 ...
        while (count > 0) {
            array[sortedIndex++] = i;
            count--;
        }
    })
    return array;
}

const test_arr6 = [2, 1, 78, 45, 62, 90, 55, 36, 45, 28];
const counting_res = countingSort(test_arr6);
console.log('计数排序:', counting_res);

function findMaxValue(array) {
    let max = array[0];
    for (let i = 1; i < array.length; i++) {
        if (array[i] > max) {
            max = array[i];
        }
    }
    return max;
}