排序算法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
冒泡(Bubble Sort) | O(n^2) | O(1) | 稳定 |
快排(Quick Sort) | O(n*logn) | O(logn) | 不稳定 |
选择(Selection Sort) | O(n^2) | O(1) | 不稳定 |
堆排(Heap Sort) | O(n*logn) | O(1) | 不稳定 |
插入(Insertion Sort) | O(n^2) | O(1) | 稳定 |
希尔(Shell Sort) | O(n^1.3) | O(1) | 不稳定 |
归并(Merge Sort) | O(n*logn) | O(n) | 稳定 |
计数(Counting Sort) | O(n+k) | O(n+k) | 稳定 |
桶排序(Bucket Sort) | O(n+n*(logn-logM)) | O(n+M) | 稳定 |
基数排序(Radix Sort) | O(d*2n) | O(n+k) | 稳定 |
#include
using namespace std;
//不稳定: 快排、堆排、选择、希尔
template<typename T, int n>
// 1、冒泡排序(Bubble Sort)时间复杂度 O(n^2) 空间复杂度 O(1)
void BubbleSort(T (&arr)[n]) {
for (int i = 0; i < n-1; ++i) {
for (int j = 0; j < n-1-i; ++j) {
if(arr[j] > arr[j+1]) swap(arr[j], arr[j+1]);
}
}
}
template<typename T, int n>
// 2、选择排序(Selection Sort)时间复杂度 O(n^2) 空间复杂度 O(1)
void SelectionSort(T (&arr)[n]) {
for (int i = 0; i < n-1; ++i) {
int minIndex = i;
for (int j = i; j < n; ++j) {
if(arr[j] < arr[minIndex]) minIndex = j;
}
swap(arr[minIndex], arr[i]);
}
}
template<typename T, int n>
// 3、插入排序(Insertion Sort)时间复杂度 O(n^2) 空间复杂度 O(1)
void InsertionSort(T (&arr)[n]) {
for (int i = 1; i < n; ++i) {
int preIndex = i-1;
T current = arr[i];
while(preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
}
template<typename T, int n>
// 4、希尔排序(Shell Sort)时间复杂度 O(n^1.3) 空间复杂度 O(1)
void ShellInsert(T (&arr)[n], int d) { // 增量为 d 的插入排序
for (int i = d; i < n; ++i) {
int preIndex = i-d;
T current = arr[i];
while(preIndex >= 0 && arr[preIndex] > current) {
arr[preIndex + d] = arr[preIndex];
preIndex -= d;
}
arr[preIndex + d] = current;
}
}
template<typename T, int n>
void ShellSort(T (&arr)[n]) {
int d = n/2;
while(d) {
ShellInsert(arr, d);
d /= 2;
}
}
template<typename T, int n>
// 5、归并排序(Merge Sort)时间复杂度 O(n*logn) 空间复杂度 O(n)
void merge(T (&arr)[n], int l, int m, int r) { // 合并两个有序数组
T tmp[n];
int i = l;
int j = m+1;
int k = 0;
while(i <= m && j <= r) {
if(arr[i] <= arr[j]) tmp[k++] = arr[i++];
else tmp[k++] = arr[j++];
}
while(i <= m) {
tmp[k++] = arr[i++];
}
while(j <= r) {
tmp[k++] = arr[j++];
}
for (int i = 0; i < r-l+1; ++i) {
arr[l+i] = tmp[i];
}
}
template<typename T, int n>
void mSort(T (&arr)[n], int l, int r) { // 分成两路排序,然后合并
if(l >= r) return ;
int m = (l+r)/2;
mSort(arr, l, m);
mSort(arr, m+1, r);
merge(arr, l, m, r);
}
template<typename T, int n>
void MergeSort(T (&arr)[n]) {
mSort(arr, 0, n-1);
}
template<typename T, int n>
// 6、快速排序(Quick Sort)时间复杂度 O(n*logn) 空间复杂度 O(logn):因为快排的实现是递归调用的, 而且每次函数调用中只使用了常数的空间,因此空间复杂度等于递归深度Θ(lgn)
int partition(T (&arr)[n], int l, int r) {
int m = arr[l]; // 基准数为第一个数
int i = l; // 不能用 i = l+1; 因为j最小等于i,如果所有数都比基数大,那么j = i = l+1,就会出现基数和第l+1个数交换的错误情况
int j = r;
while(i < j) {
while(i < j && arr[j] >= m) j--; // 由于基准数为第一个数,所以先找比基准数小的数,把比基数小的数和基数交换
while(i < j && arr[i] <= m) i++;
swap(arr[i], arr[j]);
}
swap(arr[l], arr[j]);
return j;
}
template<typename T, int n>
void qsort(T (&arr)[n], int l, int r) {
if(l >= r)
return ;
int m = partition(arr, l, r);
qsort(arr, l, m-1);
qsort(arr, m+1, r);
}
template<typename T, int n>
void QuickSort(T (&arr)[n]) {
qsort(arr, 0, n-1);
}
template<typename T, int n>
// 7、堆排序(Heap Sort)时间复杂度 O(n*logn) 空间复杂度 O(1) 不稳定性举例:{3, 4, 4}
void heapAdjust(T (&arr)[n], int l, int r) { // 只有堆顶位置不对,调整他
int dad = l;
int son = dad * 2 + 1;
while (son <= r) { // 若子節點指標在範圍內才做比較
if (son + 1 <= r && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
son++;
if (arr[dad] > arr[son]) // 如果父節點大於子節點代表調整完畢,直接跳出函數
return;
else { // 否則交換父子內容再繼續子節點和孫節點比較
swap(arr[dad], arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
// int fa = l;
// for (int i = 2*fa+1; i <= r; i=i*2+1) {
// if(i < r && arr[i] < arr[i+1]) i++;
// if(arr[fa] > arr[i]) // 已经调整完毕
// break;
// swap(arr[i], arr[fa]);
// fa = i;
// }
}
template<typename T, int n>
void HeapSort(T (&arr)[n]) {
// 建立大顶堆,自底向上调整堆
for (int i = n/2-1; i >= 0; --i) { // 从第一个非叶子结点开始
heapAdjust(arr, i, n-1);
}
// 将根节点与最后一个节点交换位置
for (int i = n-1; i > 0; --i) {
swap(arr[0], arr[i]);
heapAdjust(arr, 0, i-1);
}
}
template<typename T, int n>
// 8、计数排序(Counting Sort)时间复杂度 O(n+k) 空间复杂度 O(n+k)
void CountingSort(T (&arr)[n]) {
int maxValue = * max_element(arr, arr+n);
T *cnt = new T[maxValue+1];
memset(cnt, 0, sizeof(T)*(maxValue+1));
for (int i = 0; i < n; ++i) {
cnt[arr[i]]++;
}
int k = 0;
for (int i = 0; i <= maxValue; ++i) {
for (int j = 0; j < cnt[i]; ++j) {
arr[k++] = i;
}
}
}
template<typename T>
// 9、桶排序(Bucket Sort)对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序,时间复杂度 平均:O(N+C) C=N*(logN-logM) from:O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM) 空间复杂度 O(N+M)
int f(T x) { // 映射函数: 采用十位的数字
return int(x/10);
}
template<typename T, int n>
void BucketSort(T (&arr)[n]) {
int bucketNums = 10; // 桶的数量,这里默认为10,代表 规定待排数据范围 [0,100)
vector<T> buckets[bucketNums]; // 这里我用 vector 存桶中的数据
for (int i = 0; i < n; ++i) {
buckets[f(arr[i])].push_back(arr[i]); // 数据分桶
}
for (int i = 0; i < bucketNums; ++i) {
sort(buckets[i].begin(), buckets[i].end()); // 桶内快排
}
int k = 0;
for (int i = 0; i < bucketNums; ++i) {
for (int j = 0; j < buckets[i].size(); ++j) {
arr[k++] = buckets[i][j];
}
}
}
template<typename T, int n>
// 10、基数排序(Radix Sort)假如待排数据可以分为d个关键字,则基数排序的时间复杂度:O(d*2n) 空间复杂度 O(n+k)
vector<T>* distribute(T (&arr)[n], int bit) { // 按第 bit 位分配
vector<T> *buckets = new vector<T>[10];
for (int j = 0; j < 10; ++j)
buckets[j].clear();
for (int i = 0; i < n; ++i) {
int num = (arr[i]/int(pow(10, bit-1)))%10;
buckets[num].push_back(arr[i]);
}
return buckets;
}
template<typename T, int n>
void collect(T (&arr)[n], vector<T>* buckets) { // 将分配好的重新收集到 arr[] 中
int k = 0;
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < buckets[i].size(); ++j) {
arr[k++] = buckets[i][j];
}
}
}
template<typename T, int n>
int getMaxBit(T (&arr)[n]) { // 在所有的数据中,找出最大的位数
T maxValue = *max_element(arr, arr+n);
int maxBit = 0;
while(maxValue) {
maxBit++;
maxValue /= 10;
}
return maxBit;
}
template<typename T, int n>
void RadixSort(T (&arr)[n]) {
int maxBit = getMaxBit(arr);
for (int i = 1; i <= maxBit; ++i) { // 从个位开始
vector<T>* buckets = distribute(arr, i); // 分配
collect(arr, buckets); // 收集
}
}
int main(int argc, char const *argv[])
{
int a[] = {3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48};
int n = 15;
// BubbleSort(a);
// SelectionSort(a);
// InsertionSort(a);
// ShellSort(a);
// MergeSort(a);
// QuickSort(a);
// HeapSort(a);
// CountingSort(a);
// BucketSort(a);
RadixSort(a);
for (int i = 0; i < n; ++i) {
cout<<a[i]<<" ";
}cout<<endl;
return 0;
}
十大经典排序算法(动图演示)
面试中的 10 大排序算法总结
快速排序的时间和空间复杂度
内部排序算法总结