交换排序(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;
}
优化点
- 使用
swapped
标记,若某一轮没有发生交换,则提前结束排序,减少不必要的循环。 - 每次遍历后,最大元素已到正确位置,因此每轮排序时只需比较未排序部分。
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;
}
优化点
- 随机化基准:
- 选择中位数或随机元素作为基准,以减少最坏情况的发生。
- 三向切分(用于重复元素):
- 若数组包含大量相同元素,三向切分快速排序(3-way quicksort)可以提高效率。
总结
排序算法 | 时间复杂度(最坏) | 时间复杂度(最好) | 时间复杂度(平均) | 空间复杂度 | 是否稳定 |
---|---|---|---|---|---|
冒泡排序 | O(n²) | O(n) | O(n²) | O(1) | 稳定 |
快速排序 | O(n²) | O(n log n) | O(n log n) | O(log n) | 不稳定 |
- 冒泡排序 适用于小数据量且希望排序过程可视化的情况。
- 快速排序 是最优交换排序算法,当数据规模较大时,平均情况下比冒泡排序快得多。
选择何种排序算法?
- 数据规模小(n < 1000): 使用冒泡排序(若数据可能已经接近有序)。
- 数据规模大(n > 1000): 使用快速排序(递归实现的原生方式,无需额外空间)。
- 数据中有大量相同元素: 优化快速排序(3-way partitioning) 处理重复数据。
这样,我们完整地介绍了 交换排序(Exchange Sort),包括 冒泡排序和快速排序,以及它们的优化点和代码实现。