交换排序


交换排序(Exchange Sort)

交换排序是一类通过交换元素位置来完成排序的排序算法,主要包括 冒泡排序(Bubble Sort)快速排序(Quick Sort)。这两种排序算法都是基于交换的思想,不需要额外的存储空间,因此在内存使用上是原生的、就地(in-place)的。


1. 冒泡排序(Bubble Sort)

基本原理

  • 遍历数组,相邻两个元素进行比较,若顺序错误则交换。
  • 每一轮遍历后,当前最大的元素会移动到正确的位置。
  • 经过 n-1 轮后,整个数组变得有序。

时间复杂度

  • 最坏情况(逆序排列):O(n²)
  • 最好情况(已经有序):O(n)
  • 平均时间复杂度:O(n²)

代码实现

#include <iostream>

using namespace std;

// 冒泡排序
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false; // 标记是否发生了交换
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) { // 交换相邻元素
                swap(arr[j], arr[j + 1]);
                swapped = true;
            }
        }
        if (!swapped) break; // 若没有发生交换,说明已排序完成,提前结束
    }
}

// 输出数组
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr) / sizeof(arr[0]);

    cout << "原始数组: ";
    printArray(arr, n);

    bubbleSort(arr, n);

    cout << "排序后的数组: ";
    printArray(arr, n);

    return 0;
}

优化点

  1. 使用 swapped 标记,若某一轮没有发生交换,则提前结束排序,减少不必要的循环。
  2. 每次遍历后,最大元素已到正确位置,因此每轮排序时只需比较未排序部分。

2. 快速排序(Quick Sort)

基本原理

  • 选择一个基准(pivot),通常取数组的最后一个元素中间元素
  • 通过分区(partition),将比基准小的放左边,比基准大的放右边。
  • 递归地对左右子数组进行快速排序。

时间复杂度

  • 最坏情况(已排序,选取极端 pivot):O(n²)
  • 最好情况(每次均衡分割):O(n log n)
  • 平均时间复杂度:O(n log n)

代码实现

#include <iostream>

using namespace std;

// 分区函数:将数组分为两部分
int partition(int arr[], int low, int high) {
    int pivot = arr[high]; // 选取最后一个元素作为基准
    int i = low - 1; // i 代表小于 pivot 部分的最后索引

    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) { // 若当前元素小于 pivot,则交换
            i++;
            swap(arr[i], arr[j]);
        }
    }
    swap(arr[i + 1], arr[high]); // 把 pivot 放到正确位置
    return i + 1; // 返回 pivot 的索引
}

// 递归快速排序
void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pivotIndex = partition(arr, low, high); // 获取基准元素索引
        quickSort(arr, low, pivotIndex - 1); // 递归左部分
        quickSort(arr, pivotIndex + 1, high); // 递归右部分
    }
}

// 输出数组
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {10, 7, 8, 9, 1, 5};
    int n = sizeof(arr) / sizeof(arr[0]);

    cout << "原始数组: ";
    printArray(arr, n);

    quickSort(arr, 0, n - 1);

    cout << "排序后的数组: ";
    printArray(arr, n);

    return 0;
}

优化点

  1. 随机化基准:
    • 选择中位数或随机元素作为基准,以减少最坏情况的发生。
  2. 三向切分(用于重复元素):
    • 若数组包含大量相同元素,三向切分快速排序(3-way quicksort)可以提高效率。

总结

排序算法 时间复杂度(最坏) 时间复杂度(最好) 时间复杂度(平均) 空间复杂度 是否稳定
冒泡排序 O(n²) O(n) O(n²) O(1) 稳定
快速排序 O(n²) O(n log n) O(n log n) O(log n) 不稳定
  • 冒泡排序 适用于小数据量且希望排序过程可视化的情况。
  • 快速排序最优交换排序算法,当数据规模较大时,平均情况下比冒泡排序快得多。

选择何种排序算法?

  1. 数据规模小(n < 1000): 使用冒泡排序(若数据可能已经接近有序)。
  2. 数据规模大(n > 1000): 使用快速排序(递归实现的原生方式,无需额外空间)。
  3. 数据中有大量相同元素: 优化快速排序(3-way partitioning) 处理重复数据。

这样,我们完整地介绍了 交换排序(Exchange Sort),包括 冒泡排序和快速排序,以及它们的优化点和代码实现。


文章作者: Gustavo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 Gustavo !
评论
  目录