交换排序
简述
交换排序是根据序列中两个元素的比较结果来对换着两个元素在序列中的位置。主要的算法有冒泡排序和快速排序。
冒泡排序
算法思想
在待排序的序列中从前往后两两比较相邻元素的值,若为逆序,则交换它们。这样完整的一趟扫描称为一趟冒泡,一趟结束后,序列中最大的元素会被交换到序列最后的位置,下一趟则比较到该位置之前即可。重复多趟,直至没有元素需要交换为止。
算法性能分析
- 空间效率:使用常数个辅助单元,空间复杂度为\(O(1)\)。
- 时间效率:最好的情况下,需要进行一趟扫描,时间复杂度为\(O(n)\);最坏的情况下,比较次数是\(\sum_{i=1}^{n-1}{(n-i)}\),移动次数是\(\sum_{i=1}^{n-1}{3(n-i)}\),故时间复杂度为\(O(n^2)\)。平均时间复杂度为\(O(n^2)\)。
- 稳定性:当相邻元素相等时,不会发生交换,因此是稳定的。
C++实现
#include <iostream>
using namespace std;
void bubbleSort(int a[], int n) {
int lastChange = n - 1, change; // 标记上一次修改的位置
for (int i = 0; i < n - 1; ++i) {
change = 0; // 清零,如果这一趟排序中没有发生交换则结束
for (int j = 0; j < lastChange; ++j) {
if (a[j] > a[j + 1]) {
swap(a[j], a[j + 1]);
change = j + 1; // 记录改变的位置
}
}
lastChange = change; // 更新记录
}
}
int main() {
const int SIZE = 10;
int a[SIZE] = { 0 };
for (int i = 0; i < SIZE; ++i) {
a[i] = rand() % 10;
cout << a[i] << " ";
}
cout << "\n\n";
bubbleSort(a, SIZE);
for (auto elem : a)
cout << elem << " ";
return 0;
}
快速排序
算法思想
快速排序是对冒泡排序的一种改进。基本思想是基于分治法:在待排序列中任取一个元素pivot作为基准,通过一趟排序将待排序列分为左右两个部分,左边的部分都小于pivot,右边的部分都大于或等于pivot,将pivot则放置在两部分的中间位置,这个过程称为一趟快速排序。然后再对其左边部分和右边部分分别递归进行快速排序。
算法性能分析
- 空间效率:由于快速排序是递归的,需要借助递归工作栈来保存每一层递归的必要信息。最好的情况下为\(\lceil log_2(n+1) \rceil\);最坏的情况为\(O(n)\);平均情况下,为\(O(log_2n)\)。
- 时间效率:如果最大不对称发生在每一层递归上,就是最坏情况,时间复杂度为\(O(n^2)\)。而在pivot选取合理的情况下,快速排序的速度会大大提升,达到\(O(nlog_2n)\)。
- 稳定性:如果右端区间两个元素相同,且均小于pivot,则在交换到左端区间后,它们的相对位置会发生变化,因此快速排序是不稳定的。
C++实现
#include <iostream>
using namespace std;
void quickSort(int a[], int low, int high) {
if (low < high) { // 如果中间还有元素则进行排序
int pivot = a[low]; // 取基准元素
int i = low, j = high;
while (i < j) {
while (i < j && pivot < a[j]) // 从右往左扫描,直到找到比pivot小的元素
--j;
if (i < j)
a[i++] = a[j];
while (i < j && a[i] < pivot) // 从左往右扫描,直到找到比pivot大的元素
++i;
if (i < j)
a[j--] = a[i];
}
a[i] = pivot;
quickSort(a, low, i - 1); // 对两个子区间继续排序
quickSort(a, i + 1, high);
}
}
int main() {
const int SIZE = 10;
int a[SIZE] = { 0 };
for (int i = 0; i < SIZE; ++i) {
a[i] = rand() % 10;
cout << a[i] << " ";
}
cout << "\n\n";
quickSort(a, 0, SIZE - 1);
for (auto elem : a)
cout << elem << " ";
return 0;
}