各种排序方法总结

目录

[1. 冒泡排序 (Bubble Sort](#1. 冒泡排序 (Bubble Sort)

[2. 选择排序 (Selection Sort)](#2. 选择排序 (Selection Sort))

[3. 插入排序 (Insertion Sort)](#3. 插入排序 (Insertion Sort))

[4. 快速排序 (Quick Sort)](#4. 快速排序 (Quick Sort))

[5. 归并排序 (Merge Sort)](#5. 归并排序 (Merge Sort))

[6. 堆排序 (Heap Sort)](#6. 堆排序 (Heap Sort))


|----------|----------------------------------------------------|-----------|-----------------------------------------------------|
| 排序算法 | 时间复杂度 | 空间复杂度 | 备注 |
| 冒泡排序 | 最好情况: O(n) 平均情况: O(n^2) 最坏情况: O(n^2) | O(1) | 原地排序,只需常量级额外空间 |
| 选择排序 | 最好情况: O(n^2) 平均情况: O(n^2) 最坏情况: O(n^2) | O(1) | 原地排序,只需常量级额外空间 |
| 插入排序 | 最好情况: O(n) 平均情况: O(n^2) 最坏情况: O(n^2) | O(1) | 原地排序,只需常量级额外空间 |
| 快速排序 | 最好情况: O(n log n) 平均情况: O(n log n) 最坏情况: O(n^2 | O(log n) | 递归调用栈空间,最好情况O(log n),最坏情况O(n),但平均情况为O(log n) |
| 归并排序 | 最好情况:O(n log n) 平均情况: O(n log n) 最坏情况: O(n log n) | O(n) | 需要额外的临时数组来存储合并结果 |
| 堆排序 | 最好情况: O(n log n) 平均情况: O(n log n) 最坏情况: O(n log n) | O(1) | 原地排序,堆的调整过程在数组内部进行,只需常量级额外空间(不考虑递归实现,若考虑递归则与快速排序类似) |

1. 冒泡排序 (Bubble Sort

时间复杂度:

  • 最好情况: O(n)
  • 平均情况: O(n^2)
  • 最坏情况: O(n^2)
javascript 复制代码
function bubbleSort(arr) {  
    let n = arr.length;  
    for (let i = 0; i < n - 1; i++) {  
        for (let j = 0; j < n - 1 - i; j++) {  
            if (arr[j] > arr[j + 1]) {  
                // 交换元素  
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];  
            }  
        }  
    }  
    return arr;  
}

2. 选择排序 (Selection Sort)

原理:

选择排序是一种简单直观的排序算法。它的工作原理是首先在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(或最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

具体步骤如下:

  1. 从未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(或最大)元素,放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

特点:

  • 选择排序的时间复杂度为O(n^2),其中n是待排序元素的数量。
  • 选择排序是一种原地排序算法,因为它只需要一个额外的空间来存储当前找到的最小(或最大)元素。
  • 选择排序不是稳定的排序算法,因为相同元素的相对位置可能会在排序过程中发生改变。

时间复杂度:

  • 最好情况: O(n^2)
  • 平均情况: O(n^2)
  • 最坏情况: O(n^2)
javascript 复制代码
function selectionSort(arr) {  
    let n = arr.length;  
    for (let i = 0; i < n - 1; i++) {  
        let minIndex = i;  
        for (let j = i + 1; j < n; j++) {  
            if (arr[j] < arr[minIndex]) {  
                minIndex = j;  
            }  
        }  
        // 交换元素  
        [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];  
    }  
    return arr;  
}

3. 插入排序 (Insertion Sort)

原理:

插入排序的工作方式是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,找到相应位置并插入时,不需要移动元素,只需将要插入的元素移动到插入点即可。

具体步骤如下:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,则将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5,直到所有元素均排序完毕。

特点:

  • 插入排序的时间复杂度为O(n^2),在数据规模较小时表现良好,特别是当数据基本有序时,时间复杂度可以接近O(n)。
  • 插入排序是一种原地排序算法,因为它只需要一个额外的空间来存储当前正在插入的元素。
  • 插入排序是稳定的排序算法,因为相同元素的相对位置在排序过程中不会发生改变。

时间复杂度:

  • 最好情况: O(n)
  • 平均情况: O(n^2)
  • 最坏情况: O(n^2)
javascript 复制代码
function insertionSort(arr) {  
    let n = arr.length;  
    for (let i = 1; i < n; i++) {  
        let key = arr[i];  
        let j = i - 1;  
        while (j >= 0 && arr[j] > key) {  
            arr[j + 1] = arr[j];  
            j = j - 1;  
        }  
        arr[j + 1] = key;  
    }  
    return arr;  
}

4. 快速排序 (Quick Sort)

原理:

快速排序是一种通过基准划分区块,再不断交换左右项的排序方式。它采用了分治法,减少了交换的次数。快速排序的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归或迭代进行,以此让整个数列变成有序序列。

具体步骤如下:

  1. 在待排序区间找到一个基准点(pivot),一般选择数组的第一个元素、最后一个元素或者随机选择一个元素。
  2. 逐个循环数组,将小于基准的项放在左侧,将大于基准的项放在右侧。一般通过交换的方式来实现。
  3. 对基准点左侧全部项和基点右侧全部项分别通过递归(或迭代)方式重复上述步骤,直到所有数组都交换完成。

时间复杂度:

  • 最好情况: O(n log n)
  • 平均情况: O(n log n)
  • 最坏情况: O(n^2) (但可以通过随机化选择基准元素等方法优化)
javascript 复制代码
function quickSort(arr) {  
    if (arr.length <= 1) {  
        return arr;  
    }  
    let pivot = arr[Math.floor(arr.length / 2)];  
    let left = [];  
    let right = [];  
    for (let i = 0; i < arr.length; i++) {  
        if (i === Math.floor(arr.length / 2)) continue;  
        if (arr[i] < pivot) {  
            left.push(arr[i]);  
        } else {  
            right.push(arr[i]);  
        }  
    }  
    return [...quickSort(left), pivot, ...quickSort(right)];  
}

5. 归并排序 (Merge Sort)

原理:

归并排序是一种分治算法,其工作原理是将未排序的列表划分为n个子列表,每个子列表包含一个元素(包含一个元素的列表被认为是有序的),然后重复合并子列表以生成新的有序子列表,直到只剩下一个子列表。
具体步骤如下:

  1. 分解:将待排序的n个元素的序列分成两个子序列,每个子序列包含n/2个元素。
  2. 解决:使用归并排序递归地排序两个子序列。
  3. 合并:将两个已排序的子序列合并成一个最终的排序序列。

时间复杂度:

  • 最好情况: O(n log n)
  • 平均情况: O(n log n)
  • 最坏情况: O(n log n)
javascript 复制代码
function mergeSort(arr) {  
    if (arr.length <= 1) {  
        return arr;  
    }  
    const middle = Math.floor(arr.length / 2);  
    const left = arr.slice(0, middle);  
    const right = arr.slice(middle);  
    return merge(mergeSort(left), mergeSort(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));  
}

6. 堆排序 (Heap Sort)

堆排序(Heap Sort)是一种基于堆(Heap)这种数据结构的比较排序算法。堆是一个近似完全二叉树的结构,分为最大堆(Max Heap)和最小堆(Min Heap)。在最大堆中,每个节点的值都大于或等于其子节点的值;在最小堆中,每个节点的值都小于或等于其子节点的值。堆排序通常使用最大堆来实现升序排序

堆排序原理:

  1. 构建最大堆:
  • 将数组看作一个完全二叉树,构建最大堆。
  • 从最后一个非叶子节点开始,向上依次调整堆,使得每个子树都满足最大堆的性质。

2.堆排序过程:

  • 将堆顶元素(最大值)与堆的最后一个元素交换。
  • 堆的大小减1,重新调整堆顶元素所在的子树,使其满足最大堆的性质。
  • 重复上述步骤,直到堆的大小为1。

时间复杂度:

  • 最好情况: O(n log n)
  • 平均情况: O(n log n)
  • 最坏情况: O(n log n)
javascript 复制代码
function heapSort(arr) {  
    let n = arr.length;  
  
    // 构建最大堆  
    for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {  
        heapify(arr, n, i);  
    }  
  
    // 一个个从堆顶取出元素  
    for (let i = n - 1; i > 0; i--) {  
        // 交换当前堆顶(最大值)和最后一个元素  
        [arr[0], arr[i]] = [arr[i], arr[0]];  
        // 重新调整堆  
        heapify(arr, i, 0);  
    }  
    return arr;  
}  
  
function heapify(arr, n, i) {  
    let largest = i;  //最大子节点
    let left = 2 * i + 1;  //左子节点
    let right = 2 * i + 2;  //右子节点
  //如果左子节点存在且大于当前最大子节点
    if (left < n && arr[left] > arr[largest]) {  
        largest = left;  
    }  
  //如果右子节点存在且大于当前最大子节点
    if (right < n && arr[right] > arr[largest]) {  
        largest = right;  
    }  
 //如果最大值不是当前子节点,则交换 
    if (largest !== i) {  
        [arr[i], arr[largest]] = [arr[largest], arr[i]];  
        heapify(arr, n, largest);  
    }  
}
相关推荐
int型码农3 小时前
数据结构第八章(一) 插入排序
c语言·数据结构·算法·排序算法·希尔排序
UFIT3 小时前
NoSQL之redis哨兵
java·前端·算法
喜欢吃燃面3 小时前
C++刷题:日期模拟(1)
c++·学习·算法
SHERlocked934 小时前
CPP 从 0 到 1 完成一个支持 future/promise 的 Windows 异步串口通信库
c++·算法·promise
怀旧,4 小时前
【数据结构】6. 时间与空间复杂度
java·数据结构·算法
积极向上的向日葵4 小时前
有效的括号题解
数据结构·算法·
GIS小天4 小时前
AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年6月7日第101弹
人工智能·算法·机器学习·彩票
_Itachi__4 小时前
LeetCode 热题 100 74. 搜索二维矩阵
算法·leetcode·矩阵
不忘不弃4 小时前
计算矩阵A和B的乘积
线性代数·算法·矩阵
不爱写代码的玉子4 小时前
HALCON透视矩阵
人工智能·深度学习·线性代数·算法·计算机视觉·矩阵·c#