排序算法有多少种?史上最全排序算法在这里!!

312 阅读9分钟

最近哈士奇在刷题的过程中看到了很多排序算法的题目,那么究竟有哪些排序算法呢?哈士奇在翻阅资料的过程中发现了十种算法,并且将其总结了出来,下面和大家分享哈士奇学到的排序算法。

1. 冒泡排序(Bubble Sort)

冒泡排序是一种简单直观的排序算法。它重复地遍历待排序数组,比较相邻的两个元素,并依次将较大(或较小)的元素交换到右侧,直到整个数组排序完成。

优点

  • 实现简单,容易理解和实现。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 效率较低,时间复杂度为O(n^2),不适用于大规模数据。
  • 不适合已经基本有序的数据集。

示例

function bubbleSort(arr) {
    const len = arr.length;
    for (let i = 0; i < len - 1; i++) {
        for (let j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(bubbleSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于简单排序需求且数据量较小的情况。

2. 选择排序(Selection Sort)

选择排序是一种简单直观的排序算法,它每次从未排序部分选择最小(或最大)的元素,与未排序部分的第一个元素交换位置,直到整个数组排序完成。

优点

  • 实现简单,容易理解和实现。
  • 不占用额外的内存空间。

缺点

  • 效率较低,时间复杂度为O(n^2)。
  • 不适合大规模数据集。

示例

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

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(selectionSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于简单排序需求且数据量较小的情况。

3. 插入排序(Insertion Sort)

插入排序是一种简单直观的排序算法,它通过构建有序序列,对于未排序的数据逐个插入到已排序序列的合适位置。

优点

  • 对于小规模数据或基本有序的数据效果较好。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 效率较低,时间复杂度为O(n^2)。
  • 不适合大规模数据集。

示例

function insertionSort(arr) {
    const len = arr.length;
    for (let i = 1; i < len; i++) {
        let key = arr[i];
        let j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
    return arr;
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(insertionSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于小规模数据或基本有序的情况。

4. 希尔排序(Shell Sort)

希尔排序是插入排序的一种改进版本,它通过将整个序列分割成若干个子序列进行插入排序,最后再对整个序列进行一次插入排序。

优点

  • 相对于插入排序,希尔排序在大规模数据时有较好的性能。
  • 不稳定排序算法,可能改变相等元素的相对位置。

缺点

  • 实现稍复杂,不如其他简单排序算法易于理解。

示例

function shellSort(arr) {
    const len = arr.length;
    let gap = Math.floor(len / 2);
    while (gap > 0) {
        for (let i = gap; i < len; i++) {
            let temp = arr[i];
            let j = i;
            while (j >= gap && arr[j - gap] > temp) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = temp;
        }
        gap = Math.floor(gap / 2);
    }
    return arr;
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(shellSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于大规模数据的排序需求。

5.归并排序(Merge Sort)

归并排序采用分治法,将问题分解成较小的子问题,然后递归地解决子问题,最后将解决方案合并起来得到原问题的解决方案。

优点

  • 时间复杂度为O(nlogn),效率较高。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 需要额外的内存空间来存储中间结果。
  • 实现稍复杂,不如其他简单排序算法易于理解。

示例

function mergeSort(arr) {
    if (arr.length <= 1) {
        return arr;
    }

    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));

    return merge(left, right);
}

function merge(left, right) {
    let result = [];
    let leftIndex = 0;
    let rightIndex = 0;

    while (leftIndex < left.length && rightIndex < right.length) {
        if (left[leftIndex] < right[rightIndex]) {
            result.push(left[leftIndex]);
            leftIndex++;
        } else {
            result.push(right[rightIndex]);
            rightIndex++;
        }
    }

    return result.concat(left.slice(leftIndex)).concat(right.slice(rightIndex));
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(mergeSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于任意大小的数据集,尤其在需要稳定排序算法的情况下。

6.堆排序(Heap Sort)

堆排序利用堆这种数据结构进行排序。它将待排序的序列构造成一个大顶堆(或小顶堆),然后依次取出堆顶元素,再调整堆,直到所有元素排序完成。

优点

  • 时间复杂度为O(nlogn),效率较高。
  • 不需要额外的存储空间。

缺点

  • 不稳定排序算法,可能改变相等元素的相对位置。
  • 实现较复杂,需要理解和实现堆的数据结构。

示例

function heapSort(arr) {
    buildMaxHeap(arr);

    for (let i = arr.length - 1; i > 0; i--) {
        [arr[0], arr[i]] = [arr[i], arr[0]];
        maxHeapify(arr, 0, i);
    }

    return arr;
}

function buildMaxHeap(arr) {
    for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
        maxHeapify(arr, i, arr.length);
    }
}

function maxHeapify(arr, i, length) {
    let left = 2 * i + 1;
    let right = 2 * i + 2;
    let largest = i;

    if (left < length && arr[left] > arr[largest]) {
        largest = left;
    }

    if (right < length && arr[right] > arr[largest]) {
        largest = right;
    }

    if (largest !== i) {
        [arr[i], arr[largest]] = [arr[largest], arr[i]];
        maxHeapify(arr, largest, length);
    }
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(heapSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于需要稳定排序算法且数据量较大的情况。

7.快速排序(Quick Sort)

快速排序采用分治法,选择一个基准元素,将小于基准的元素放在左侧,大于基准的元素放在右侧,然后递归地对左右两侧的子数组进行排序。

优点

  • 时间复杂度为O(nlogn),效率较高。
  • 原地排序算法,不需要额外的存储空间。

缺点

  • 不稳定排序算法,可能改变相等元素的相对位置。
  • 对于已经基本有序的数组,效率较低。

示例

function quickSort(arr, left = 0, right = arr.length - 1) {
    if (left < right) {
        const pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }
    return arr;
}

function partition(arr, left, right) {
    const pivot = arr[right];
    let i = left;
    for (let j = left; j < right; j++) {
        if (arr[j] < pivot) {
            [arr[i], arr[j]] = [arr[j], arr[i]];
            i++;
        }
    }
    [arr[i], arr[right]] = [arr[right], arr[i]];
    return i;
}

const arr = [64, 34, 25, 12, 22, 11, 90];
console.log(quickSort(arr)); // [11, 12, 22, 25, 34, 64, 90]

应用场景:适用于任意大小的数据集,尤其在需要原地排序算法的情况下。

8.计数排序(Counting Sort)

计数排序是一种非比较排序算法,它通过确定每个元素在输入序列中的位置来排序。它适用于元素范围较小且已知的情况。

优点

  • 时间复杂度为O(n+k),其中n是元素的数量,k是元素的范围,效率较高。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 需要额外的存储空间来存储计数数组。
  • 仅适用于元素范围较小且已知的情况。

示例

function countingSort(arr) {
    const max = Math.max(...arr);
    const min = Math.min(...arr);
    const count = Array(max - min + 1).fill(0);
    const output = [];

    arr.forEach(num => count[num - min]++);
    count.forEach((num, index) => {
        for (let i = 0; i < num; i++) {
            output.push(index + min);
        }
    });

    return output;
}

const arr = [4, 2, 2, 8, 3, 3, 1];
console.log(countingSort(arr)); // [1, 2, 2, 3, 3, 4, 8]

应用场景:适用于元素范围较小且已知的情况,例如对字符进行排序。

9.桶排序(Bucket Sort)

桶排序将元素分配到有限数量的桶中,对每个桶中的元素进行排序,最后将所有桶中的元素按照顺序合并。

优点

  • 对于均匀分布的数据,效率较高。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 需要额外的存储空间来存储桶。
  • 对于数据分布不均匀的情况,可能效率较低。

示例

function bucketSort(arr, bucketSize = 5) {
    if (arr.length === 0) {
        return arr;
    }

    const min = Math.min(...arr);
    const max = Math.max(...arr);
    const bucketCount = Math.floor((max - min) / bucketSize) + 1;
    const buckets = Array.from({ length: bucketCount }, () => []);

    arr.forEach(num => {
        const bucketIndex = Math.floor((num - min) / bucketSize);
        buckets[bucketIndex].push(num);
    });

    return buckets.reduce((acc, bucket) => acc.concat(bucket.sort((a, b) => a - b)), []);
}

const arr = [29, 25, 3, 49, 9, 37, 21, 43];
console.log(bucketSort(arr)); // [3, 9, 21, 25, 29, 37, 43, 49]

应用场景:适用于均匀分布的数据,例如对学生成绩进行排序。

10.基数排序(Radix Sort)

基数排序是一种多关键字的排序算法,它根据每个关键字的值来排序。它从最低有效位(或者最高有效位)开始,依次对每个关键字进行排序。

优点

  • 对于整数或字符串等具有多关键字的数据,效率较高。
  • 稳定排序算法,不会改变相等元素的相对位置。

缺点

  • 对于关键字位数较大的数据,可能需要较大的存储空间。
  • 实现相对复杂,不如其他简单排序算法易于理解。

示例

function radixSort(arr) {
    const max = Math.max(...arr);
    let exp = 1;
    while (Math.floor(max / exp) > 0) {
        arr = countingSortForRadix(arr, exp);
        exp *= 10;
    }
    return arr;
}

function countingSortForRadix(arr, exp) {
    const output = [];
    const count = Array(10).fill(0);

    arr.forEach(num => {
        const digit = Math.floor(num / exp) % 10;
        count[digit]++;
    });

    for (let i = 1; i < 10; i++) {
        count[i] += count[i - 1];
    }

    for (let i = arr.length - 1; i >= 0; i--) {
        const digit = Math.floor(arr[i] / exp) % 10;
        output[count[digit] - 1] = arr[i];
        count[digit]--;
    }

    return output;
}

const arr = [329, 457, 657, 839, 436, 720, 355];
console.log(radixSort(arr)); // [329, 355, 436, 457, 657, 720, 839]

应用场景:适用于需要多关键字排序的情况,例如对字符串进行排序。

以上就是哈士奇介绍的所有排序算法,假如您也和我一样,在准备春招。欢迎加我微信shunwuyu ,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!