排序算法全景总结:从 O(n²) 到 O(n log n),再到线性时间(附完整对比与选型指南)

一、排序算法分类

1. 比较类排序

通过元素之间的比较来决定顺序,理论下界是 Ω(n log n)。

  • 交换类:冒泡排序、快速排序
  • 选择类:简单选择排序、堆排序
  • 插入类:直接插入排序、希尔排序
  • 归并类:归并排序

2. 非比较类排序

不直接比较元素大小,而是利用数据本身的特性(范围、位数等),可以突破 O(n log n) 的限制,达到线性时间。

  • 计数排序
  • 桶排序
  • 基数排序

二、各大排序算法详解与对比

1. 冒泡排序(Bubble Sort)

思想:反复比较相邻元素,把较大的元素逐步"冒泡"到后面。

  • 时间复杂度:最好 O(n),平均/最坏 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 特点:实现简单,可优化提前结束,但效率低

适用:数据量很小,或几乎已经有序。

2. 选择排序(Selection Sort)

思想:每一轮从剩余元素中选出最小值,放到已排序部分的末尾。

  • 时间复杂度:始终 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 特点:交换次数少(最多 n-1 次)

适用:交换成本较高的场景。

3. 插入排序(Insertion Sort)

思想:把数组分成已排序和未排序两部分,每次把未排序的第一个元素插入到已排序部分的正确位置。

  • 时间复杂度:最好 O(n),平均/最坏 O(n²)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 特点:对近乎有序的数组效率极高

适用:小规模数据,或作为快速排序的优化(小数组转插入排序)。

4. 希尔排序(Shell Sort)

思想:插入排序的改进版。通过设定增量序列进行分组插入,逐步缩小增量。

  • 时间复杂度:约 O(n^1.3)(取决于增量序列)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 特点:比直接插入排序快很多,实现简单

适用:中等规模数据。

5. 堆排序(Heap Sort)

思想:利用堆结构,先建大顶堆,反复把堆顶最大值与末尾交换,再调整堆。

  • 时间复杂度:始终 O(n log n)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
  • 特点:原地排序,时间稳定

适用:大数据量且内存受限的场景。

6. 归并排序(Merge Sort)

思想:分治。把数组分成两半,分别排序,再合并两个有序数组。

  • 时间复杂度:始终 O(n log n)
  • 空间复杂度:O(n)
  • 稳定性:稳定
  • 特点:时间稳定,适合链表排序

适用:需要稳定排序,或数据量大且可以接受额外空间。

7. 快速排序(Quick Sort)

思想:分治 + 分区。选基准值,把小于基准的放左边,大于基准的放右边,再递归。

  • 时间复杂度:平均 O(n log n),最坏 O(n²)
  • 空间复杂度:O(log n)
  • 稳定性:不稳定
  • 特点:实际运行通常最快,工程首选

优化手段

  • 随机基准 / 三数取中
  • 小数组转插入排序
  • 尾递归优化

适用:绝大多数通用场景。

8. 计数排序(Counting Sort)

思想:统计每个元素出现的次数,然后按顺序放回。

  • 时间复杂度:O(n + k)(k 为数据范围)
  • 空间复杂度:O(k)
  • 稳定性:稳定
  • 特点:非比较排序,速度极快

适用:数据范围不大的整数排序。

9. 桶排序(Bucket Sort)

思想:把数据分到有限数量的桶里,每个桶内部再排序,最后合并。

  • 时间复杂度:平均 O(n + k),最坏 O(n²)
  • 空间复杂度:O(n + k)
  • 稳定性:稳定(取决于桶内排序)
  • 特点:对数据分布均匀时效果好

适用:数据分布比较均匀的场景。

10. 基数排序(Radix Sort)

思想:按照个位、十位、百位等从低位到高位依次进行稳定排序(通常用计数排序)。

  • 时间复杂度:O(d(n + k))(d 为位数)
  • 空间复杂度:O(n + k)
  • 稳定性:稳定
  • 特点:非比较排序,适合位数有限的整数

适用:整数、字符串(定长)、手机号、学号等。

三、终极对比表

算法 平均时间 最坏时间 空间 稳定 原地 主要特点
冒泡 O(n²) O(n²) O(1) 简单,可提前结束
选择 O(n²) O(n²) O(1) 交换次数少
插入 O(n²) O(n²) O(1) 近乎有序时很快
希尔 O(n^1.3) O(n²) O(1) 插入排序的改进
堆排序 O(n log n) O(n log n) O(1) 时间稳定,原地
归并 O(n log n) O(n log n) O(n) 稳定,适合链表
快速 O(n log n) O(n²) O(log n) 实际最快,通用首选
计数 O(n+k) O(n+k) O(k) 范围小时极快
桶排序 O(n+k) O(n²) O(n+k) 数据分布均匀时高效
基数 O(d(n+k)) O(d(n+k)) O(n+k) 位数有限的整数

四、实际选型建议(非常重要)

  1. 通用情况std::sort(底层通常是快速排序 + 堆排序 + 插入排序的混合,Introsort)
  2. 需要稳定排序 → 归并排序 或 基数排序
  3. 数据量很小 → 插入排序
  4. 数据近乎有序 → 插入排序
  5. 内存极度受限 → 堆排序
  6. 数据是整数且范围小 → 计数排序
  7. 数据是整数且位数不多 → 基数排序
  8. 数据分布均匀 → 桶排序

五、十大排序算法完整可运行 C++ 代码合集

下面给出十大排序算法的完整 C++ 实现,统一风格整理,可直接复制到一个文件里编译运行。

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;

// ==================== 工具函数 ====================
void printArray(const vector<int>& arr, const string& name = "") {
    if (!name.empty()) cout << name << ":";
    for (int x : arr) cout << x << " ";
    cout << endl;
}

// ==================== 1. 冒泡排序 ====================
void BubbleSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; ++i) {
        bool swapped = false;
        for (int j = 0; j < n - 1 - i; ++j) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break; // 优化:提前结束
    }
}

// ==================== 2. 选择排序 ====================
void SelectionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; ++i) {
        int minIdx = i;
        for (int j = i + 1; j < n; ++j) {
            if (arr[j] < arr[minIdx]) minIdx = j;
        }
        if (minIdx != i) swap(arr[i], arr[minIdx]);
    }
}

// ==================== 3. 插入排序 ====================
void InsertionSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 1; i < n; ++i) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            --j;
        }
        arr[j + 1] = key;
    }
}

// ==================== 4. 希尔排序 ====================
void ShellSort(vector<int>& arr) {
    int n = arr.size();
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; ++i) {
            int temp = arr[i];
            int j = i;
            while (j >= gap && arr[j - gap] > temp) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = temp;
        }
    }
}

// ==================== 5. 堆排序 ====================
void heapify(vector<int>& arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int 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) {
        swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

void HeapSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = n / 2 - 1; i >= 0; --i) heapify(arr, n, i);
    for (int i = n - 1; i > 0; --i) {
        swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}

// ==================== 6. 归并排序 ====================
void merge(vector<int>& arr, int left, int mid, int right) {
    vector<int> temp(right - left + 1);
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) temp[k++] = arr[i++];
        else temp[k++] = arr[j++];
    }
    while (i <= mid) temp[k++] = arr[i++];
    while (j <= right) temp[k++] = arr[j++];
    for (int p = 0; p < temp.size(); ++p) arr[left + p] = temp[p];
}

void MergeSort(vector<int>& arr, int left, int right) {
    if (left >= right) return;
    int mid = left + (right - left) / 2;
    MergeSort(arr, left, mid);
    MergeSort(arr, mid + 1, right);
    merge(arr, left, mid, right);
}

// ==================== 7. 快速排序 ====================
int partition(vector<int>& arr, int low, int high) {
    int pivot = arr[high];
    int i = low - 1;
    for (int j = low; j < high; ++j) {
        if (arr[j] < pivot) {
            ++i;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]);
    return i + 1;
}

void QuickSort(vector<int>& arr, int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        QuickSort(arr, low, pi - 1);
        QuickSort(arr, pi + 1, high);
    }
}

// ==================== 8. 计数排序 ====================
void CountingSort(vector<int>& arr) {
    if (arr.empty()) return;
    int maxVal = *max_element(arr.begin(), arr.end());
    int minVal = *min_element(arr.begin(), arr.end());
    int range = maxVal - minVal + 1;
    vector<int> count(range, 0);
    vector<int> output(arr.size());
    for (int x : arr) count[x - minVal]++;
    for (int i = 1; i < range; ++i) count[i] += count[i - 1];
    for (int i = arr.size() - 1; i >= 0; --i) {
        output[count[arr[i] - minVal] - 1] = arr[i];
        count[arr[i] - minVal]--;
    }
    arr = output;
}

// ==================== 9. 桶排序 ====================
void BucketSort(vector<int>& arr) {
    if (arr.empty()) return;
    int maxVal = *max_element(arr.begin(), arr.end());
    int minVal = *min_element(arr.begin(), arr.end());
    int bucketCount = max(1, (maxVal - minVal) / 10 + 1);
    vector<vector<int>> buckets(bucketCount);
    for (int x : arr) {
        int idx = (x - minVal) / 10;
        buckets[idx].push_back(x);
    }
    arr.clear();
    for (auto& bucket : buckets) {
        sort(bucket.begin(), bucket.end());
        for (int x : bucket) arr.push_back(x);
    }
}

// ==================== 10. 基数排序 ====================
void countingSortByDigit(vector<int>& arr, int exp) {
    vector<int> output(arr.size());
    vector<int> count(10, 0);
    for (int x : arr) count[(x / exp) % 10]++;
    for (int i = 1; i < 10; ++i) count[i] += count[i - 1];
    for (int i = arr.size() - 1; i >= 0; --i) {
        int digit = (arr[i] / exp) % 10;
        output[count[digit] - 1] = arr[i];
        count[digit]--;
    }
    arr = output;
}

void RadixSort(vector<int>& arr) {
    if (arr.empty()) return;
    int maxVal = *max_element(arr.begin(), arr.end());
    for (int exp = 1; maxVal / exp > 0; exp *= 10) {
        countingSortByDigit(arr, exp);
    }
}

// ==================== 主函数测试 ====================
int main() {
    vector<int> data = {64, 34, 25, 12, 22, 11, 90, 5};

    vector<int> a1 = data; BubbleSort(a1);    printArray(a1, "冒泡排序");
    vector<int> a2 = data; SelectionSort(a2); printArray(a2, "选择排序");
    vector<int> a3 = data; InsertionSort(a3); printArray(a3, "插入排序");
    vector<int> a4 = data; ShellSort(a4);     printArray(a4, "希尔排序");
    vector<int> a5 = data; HeapSort(a5);      printArray(a5, "堆排序");
    vector<int> a6 = data; MergeSort(a6, 0, a6.size() - 1); printArray(a6, "归并排序");
    vector<int> a7 = data; QuickSort(a7, 0, a7.size() - 1); printArray(a7, "快速排序");
    vector<int> a8 = data; CountingSort(a8);  printArray(a8, "计数排序");
    vector<int> a9 = data; BucketSort(a9);    printArray(a9, "桶排序");
    vector<int> a10 = data; RadixSort(a10);   printArray(a10, "基数排序");

    return 0;
}

六、总结

比较类排序的理论下界是 O(n log n),快速排序、归并排序、堆排序是其中的代表。非比较类排序可以突破这个限制,但需要数据满足特定条件。

实际工程中,快速排序(及其优化版本)是最常用的选择,稳定排序优先考虑归并。把这些算法的思想、复杂度、稳定性和适用场景真正理解后,排序这部分基本就通透了。

相关推荐
一直C1 小时前
【Linux应用编程】深入理解Linux多任务机制:进程原理、状态转换与进程控制实战
linux·开发语言·算法·ubuntu·vim·visual studio code
玉树临风ives2 小时前
atcoder ABC 472 题解
数据结构·c++·算法
AI服务老曹2 小时前
抽烟识别算法接入 AI 视频分析平台的流程与误报优化指南
人工智能·算法·音视频
阳明山水2 小时前
销量预测2025—2026:技术演进、实证发现与系统架构的系统性综述
人工智能·深度学习·算法·机器学习·架构
伯远医学2 小时前
RIP-seq 高分文献案例分享
java·前端·javascript·人工智能·算法·eclipse·html
啊嘞嘞?2 小时前
力扣(LRU缓存)
算法·leetcode
珂朵莉MM2 小时前
2026国信杯具身智能创新大赛-编程技能赛--本科组国赛解题报告 | 珂学家
算法·深度优先·图论
啊嘞嘞?2 小时前
力扣(下一个排列)
算法·leetcode