【数据结构】排序算法

在这里插入图片描述
不稳定的排序算法: 快排、堆排、选择、希尔

排序算法时间复杂度空间复杂度稳定性
冒泡(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 <bits/stdc++.h>
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 大排序算法总结
快速排序的时间和空间复杂度
内部排序算法总结

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值