这篇博客用C++实现各种排序算法,并对他们的时间复杂度以及稳定性进行分析。
所谓稳定性是指排序前后,两个相等的数的相对位置不变。
稳定排序法:冒泡排序(交换只发生在相邻的两个不等数之间),插入排序(只跟比它大的数据交换),归并排序(我们可以规定元素相等时,先放前一序列的元素)。
不稳定排序法:堆排序,选择排序(举例:序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了),快速排序(同理,中枢元素交换时会打乱相等元素的先后顺序)。
堆排序为什么破坏稳定性:堆的结构是节点i的孩子为2i和2i+1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点,是完全二叉树;在一个长为n 的序列,堆排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n /2-1, n/2-2, …1这些个父节点选择元素时,就会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了;
不稳定排序法:
- 冒泡排序
template<typename T> bool asc(T a,T b){//升序 return a>b; } template<typename T> bool desc(T a,T b){//降序 return a<b; } template<typename T> void bubblesort(T *a, int n, bool(*cmpfunc)(T, T) = asc){//设置默认为升序排序 bool sorted=false; T tmp; while(!sorted){ sorted=true; for(int i=0;i<n-1;++i){ if(cmpfunc(a[i],a[i+1])){ tmp=a[i]; a[i]=a[i+1]; a[i+1]=tmp; sorted=false; } } --n;//每次for循环都会把最大或者最小的数沉到底部,这也就少排一个数 } }
- 插入排序
#include <iostream> using namespace std; template<typename T> bool asc(T a,T b){//升序 return a>b; } template<typename T> bool desc(T a,T b){//降序 return a<b; } template<typename T> void insertsort(T *a, int n, bool(*cmpfunc)(T, T) = asc){//设置默认为升序排序 T tmp; for(int k=0;k<n-1;++k){ int i=k+1; while(i>0 && cmpfunc(a[i],a[i-1])){ tmp=a[i]; a[i]=a[i-1]; a[i-1]=tmp; --i; } } } //对函数进行调用 int main() { int a[8] = {5,2,5,7,1,-3,99,56}; int b[8] = {5,2,5,7,1,-3,99,56}; insertsort<int>(a, 8);//将函数名作为指针传递过去就行 for (auto e:a) std::cout << e << " "; std::cout << std::endl; insertsort<int>(b, 8, desc); for (auto e:b) std::cout << e << " "; return 0; }
- 归并排序
- 选择排序
#include <iostream> using namespace std; template<typename T> int asc(T *a,int lo,int hi){//升序 int idx=lo; T _min=a[lo]; for(int i=lo+1;i<=hi;++i){ if(a[i]<_min){ _min=a[i]; idx=i; } } return idx; } template<typename T> int desc(T *a, int lo,int hi){//降序 int idx=lo; T _max=a[lo]; for(int i=lo+1;i<=hi;++i){ if(a[i]>_max){ _max=a[i]; idx=i; } } return idx; } template<typename T> void selectsort(T *a, int n, int(*cmpfunc)(T *,int,int) = asc){//设置默认为升序排序 T tmp; for(int k=0;k<n;++k){ int idx=cmpfunc(a,k,n-1); if(idx!=k){ tmp=a[k]; a[k]=a[idx]; a[idx]=tmp; } } } //对函数进行调用 int main() { int a[8] = {5,2,5,7,1,-3,99,56}; int b[8] = {5,2,5,7,1,-3,99,56}; selectsort<int>(a, 8);//将函数名作为指针传递过去就行 for (auto e:a) std::cout << e << " "; std::cout << std::endl; selectsort<int>(b, 8, desc); for (auto e:b) std::cout << e << " "; return 0; }
输出结果:
-3 1 2 5 5 7 56 99
99 56 7 5 5 2 1 -3 - 快速排序
#include <iostream> using namespace std; template <typename T> void _swap(T &a,T &b){ T tmp=a; a=b; b=tmp; } template <typename T> int partition(T *a,int lo,int hi){ _swap(a[lo],a[lo+rand()%(hi-lo+1)]); T pivot=a[lo]; int mi=lo; for(int k=lo+1;k<=hi;++k){ if(a[k]<pivot){ _swap(a[k],a[++mi]); } } swap(a[lo],a[mi]); return mi; } template<typename T> void quicksort(T *a, int lo, int hi){//设置默认为升序排序 if(hi<=lo) return; int mid=partition(a,lo,hi); quicksort(a,lo,mid-1); quicksort(a,mid+1,hi); } //对函数进行调用 int main() { int a[8] = {5,2,5,7,1,-3,99,56}; int b[8] = {5,2,5,7,1,-3,99,56}; quicksort<int>(a, 0,7);//将函数名作为指针传递过去就行 for (auto e:a) std::cout << e << " "; std::cout << std::endl; quicksort<int>(b, 0,7); for (auto e:b) std::cout << e << " "; return 0; }
- 堆排序