导读:本期聚焦于兔子创作的《C++冒泡排序怎么写?C++十大经典排序算法实现代码详解》,敬请观看详情。排序算法是C++学习和面试中的高频考点,本文完整讲解冒泡排序的写法与优化思路,并附上选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、计数排序、桶排序、基数排序等十大经典排序算法的C++实现代码。每种算法都配有思路分析、时间复杂度与稳定性说明,代码可直接编译运行,适合初学者系统学习和开发者快速查阅。

冒泡排序是许多人学习C++时接触的第一个排序算法,它的原理直观、代码简短,非常适合用来理解排序的本质。但只掌握冒泡排序显然不够,面试和实际开发中经常要求对比多种排序算法的性能与适用场景。本文先详细讲解冒泡排序的标准写法和两种常见优化,再依次给出其余九大经典排序算法的完整C++实现代码,并分析它们的时间复杂度、空间复杂度和稳定性。

C++冒泡排序怎么写?C++十大经典排序算法实现代码详解

一、C++冒泡排序怎么写(含优化版本)

冒泡排序的基本思想是:从头到尾依次比较相邻的两个元素,如果前面的比后面的大就交换位置,这样一趟下来最大的元素就会“冒泡”到末尾。对n个元素执行n-1趟后,整个数组就有序了。

#include <iostream>
#include <vector>
using namespace std;

// 基础版冒泡排序
void bubbleSort(vector<int>& arr) {
    int n = arr.size();
    for (int i = 0; i < n - 1; i++) {          // 一共进行 n-1 趟
        for (int j = 0; j < n - 1 - i; j++) {  // 每趟比较到未排序部分的末尾
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);       // 逆序则交换
            }
        }
    }
}

int main() {
    vector<int> arr = {5, 2, 9, 1, 7, 3};
    bubbleSort(arr);
    for (int x : arr) cout << x << " ";
    return 0;
}</code>

基础版有一个明显缺陷:即使数组在中途已经完全有序,外层循环依然会继续执行。经典面试考点就是如何优化这一点。做法是设置一个标志位,某一趟如果没有发生任何交换,说明数组已经有序,直接退出循环。

进一步的优化是“双向冒泡”,也叫鸡尾酒排序:每趟先从左往右把最大值冒到右边,再从右往左把最小值冒到左边,对于大部分元素已有序的数组效率更高。

// 优化版:提前退出 + 记录最后交换位置
void bubbleSortOpt(vector<int>& arr) {
    int n = arr.size();
    int lastSwap = n - 1;
    while (lastSwap > 0) {
        int boundary = lastSwap;
        lastSwap = 0;
        for (int j = 0; j < boundary; j++) {
            if (arr[j] > arr[j + 1]) {
                swap(arr[j], arr[j + 1]);
                lastSwap = j;   // 记录最后一次交换的位置
            }
        }
    }
}

冒泡排序的平均和最坏时间复杂度都是O(n²),最好情况(已有序)优化后可达O(n),空间复杂度O(1),是稳定排序。数据量大时性能不佳,主要价值在于教学和理解排序思想。

二、简单排序家族:选择排序、插入排序、希尔排序

选择排序每一趟从未排序区间中选出最小元素,放到已排序区间的末尾。它的交换次数最少,最多只有n-1次交换,但不稳定,时间复杂度恒为O(n²)。

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]);
    }
}

插入排序像整理扑克牌:把新元素插入到前面已排序的合适位置。它在数据基本有序或规模很小时表现极好,这也是STL中std::sort在小区间切换到插入排序的原因。平均O(n²),最好O(n),稳定。

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;
    }
}

希尔排序是插入排序的改进版,按增量将数组分组做插入排序,增量逐渐缩小到1。它的时间复杂度依赖于增量序列,约在O(n^1.3)到O(n²)之间,空间O(1),不稳定。

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 key = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > key) {
                arr[j + gap] = arr[j];
                j -= gap;
            }
            arr[j + gap] = key;
        }
    }
}

三、高效比较排序:快速排序、归并排序、堆排序

快速排序是实践中最常用的排序之一。它选取一个基准值,将数组划分成小于和大于基准的两部分,再递归处理。平均时间复杂度O(n log n),最坏退化为O(n²),空间复杂度取决于递归深度,不稳定。

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) {
            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 p = partition(arr, low, high);
        quickSort(arr, low, p - 1);
        quickSort(arr, p + 1, high);
    }
}

归并排序采用分治思想,把数组不断二分直到单个元素,再两两合并有序子数组。它的时间复杂度稳定为O(n log n),但需要O(n)的辅助空间,并且是稳定排序,适合链表排序和外部排序场景。

void merge(vector<int>& arr, int left, int mid, int right) {
    vector<int> tmp(right - left + 1);
    int i = left, j = mid + 1, k = 0;
    while (i <= mid && j <= right) {
        tmp[k++] = (arr[i] <= arr[j]) ? arr[i++] : arr[j++];
    }
    while (i <= mid) tmp[k++] = arr[i++];
    while (j <= right) tmp[k++] = arr[j++];
    for (k = 0; k < (int)tmp.size(); k++) arr[left + k] = tmp[k];
}

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);
}

堆排序先建立大顶堆,然后每次将堆顶元素与末尾交换并重新调整堆。时间复杂度稳定在O(n log n),空间O(1),不稳定,适合对空间要求苛刻的场景。

void heapify(vector<int>& arr, int n, int i) {
    int largest = i, l = 2 * i + 1, r = 2 * i + 2;
    if (l < n && arr[l] > arr[largest]) largest = l;
    if (r < n && arr[r] > arr[largest]) largest = r;
    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);
    }
}

四、非比较排序:计数排序、桶排序、基数排序

这类算法不依赖元素间的比较,而是利用数值本身的性质,因此可以突破O(n log n)的下界,达到线性时间,但要求数据满足特定条件。

计数排序适合取值范围不大的整数。统计每个值出现的次数,再按顺序输出。时间复杂度O(n+k),k为数据范围,稳定(下面采用前缀和的稳定写法)。

void countingSort(vector<int>& arr) {
    if (arr.empty()) return;
    int maxVal = *max_element(arr.begin(), arr.end());
    vector<int> count(maxVal + 1, 0);
    for (int x : arr) count[x]++;
    for (int i = 1; i <= maxVal; i++) count[i] += count[i - 1];  // 前缀和
    vector<int> output(arr.size());
    for (int i = arr.size() - 1; i >= 0; i--) {  // 逆序保证稳定
        output[--count[arr[i]]] = arr[i];
    }
    arr = output;
}

桶排序把数据分配到若干个桶里,每个桶内部单独排序后再拼接。桶的数量和映射函数直接影响性能,均匀分布时接近O(n),最坏退化到O(n²)。它是对计数排序的推广,适合浮点数和均匀分布的数据。

void bucketSort(vector<float>& arr) {
    int n = arr.size();
    if (n == 0) return;
    float maxVal = *max_element(arr.begin(), arr.end());
    float minVal = *min_element(arr.begin(), arr.end());
    int bucketCount = 10;
    vector<vector<float>> buckets(bucketCount);
    for (float x : arr) {
        int idx = (int)((x - minVal) / (maxVal - minVal + 1e-9) * bucketCount);
        buckets[idx].push_back(x);
    }
    for (auto& b : buckets) sort(b.begin(), b.end());
    int k = 0;
    for (auto& b : buckets)
        for (float x : b) arr[k++] = x;
}

基数排序按低位到高位依次做稳定排序(通常用计数排序),适合整数或定长字符串。d位数、基数r的情况下时间复杂度为O(d(n+r)),稳定,但要求数据能按位拆解。

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) {
        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 d = (arr[i] / exp) % 10;
            output[--count[d]] = arr[i];
        }
        arr = output;
    }
}

五、十大排序算法对比与选择建议

下表汇总了十种算法的核心指标,方便快速查阅。

算法平均时间最坏时间空间稳定性
冒泡排序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²)O(log n)不稳定
归并排序O(n log n)O(n log n)O(n)稳定
堆排序O(n log n)O(n log n)O(1)不稳定
计数排序O(n+k)O(n+k)O(k)稳定
桶排序O(n)O(n²)O(n+k)稳定
基数排序O(d(n+r))O(d(n+r))O(n+r)稳定

实际编码中,如果不确定数据特点,直接使用标准库的std::sort即可,它内省排序的结合方案在各种场景下都有不错的表现。需要稳定排序时用std::stable_sort(基于归并)。手写算法更多出现在面试、竞赛和学习场景:小数据量或基本有序选插入排序,普通整型大范围数据选快速排序,内存紧张选堆排序,取值集中的整数选计数排序,浮点数均匀分布可尝试桶排序。

理解每一种排序的适用边界,比死记代码更重要。建议在掌握本文代码后,动手修改边界条件、观察不同数据分布下各算法的耗时差异,这样才能真正把排序算法内化成自己的能力。

C++冒泡排序C++排序算法十大排序算法修改时间:2026-09-11 17:44:43

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0911/54810.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。