复习一下排序
1. 计数排序
这算是最早学的排序了.
复杂度最低,但是没什么卵用.
排序范围十分有限,且只能排数.
for (int i=1;i<=n;++i){ cnt[num[i]]++;//统计次数 } for (int i=0;i<M;++i) { for (int j=1;j<=cnt[i];++j) cout<<i<<" "; } puts("");
稳定排序,时间复杂度O(n+m),空间复杂度O(n+m)$
2. 选择排序
相比计数排序,排序范围大了很多,但是复杂度变为了O(n2).
for (int i=1;i<=n;++i) { int id=i; for (int j=i+1;j<=n;++j) { if (num[j]<num[id]) id=j;//选择一个最小的 } swap(num[i],num[id]); }
不稳定排序,时间复杂度O(n2)
3. 冒泡排序
就是让大的泡泡向上冒泡的过程
虽然常数大,但是它可以在排序时判断是否已经有序来加快速度.
while (true) { bool chk=true; for (int i=1;i<n;++i) { if (num[i]>num[i+1]) swap(num[i],num[i+1]),chk=false; } if (chk) break;//有序时退出 }
稳定排序,平均时间复杂度O(n2),最好时间复杂度O(n))
4. 插入排序
把数一个个插入序列中.
插入时把比它大的挪到后面,直到找到一个比他小的.
(同为n2,但是插入排序的常数比冒泡小)
for (int i=1;i<=n;++i) { int x=num[i]; for (int j=i-1;j&&x<num[j];--j) num[j+1]=num[j];//向后移动 num[j+1]=x; }
稳定排序,平均时间复杂度O(n2),最好时间复杂度O(n))
5. 基数排序
升级版计数排序,但是还是只能排数.
int num[M] int s[10][M],len[M]={0}; for (int sz=1;;) { bool chk=false; for (int i=1;i<=n;++i) { chk|=num[i]/sz; int k=num[i]/sz%10; s[k][len[k]++]=num[i]; } if (!chk) break; int id=0; for (int i=0;i<10;++i) { for (int j=0;j<len[i];++j) { num[++id]=s[i][j]; } len[i]=0; } sz*=10; }
稳定排序,时间复杂度O(n∗m),m为长度.
6. 归并排序
复杂度稳定的O(nlogn)排序,
用了分治的思想,把一个问题分解为小规模的子问题,
直到子问题可以直接解决,再把子问题合并解决大问题.
merge(int L,int R) { if (L==R) return; int mid=(L+R)>>1; merge(L,mid),merge(mid+1,R); int i=L,j=mid+1; int tot=0; while (i<=mid&&j<=R) { if (num[i]<num[j]) tmp[tot++]=num[i++]; else tmp[tot++]=num[j++]; } while (i<=mid) tmp[tot++]=num[i++]; while (j<=R) tmp[tot++]=num[j++]; for (int i=0;i<tot;++i) num[i+L]=tmp[i]; }
稳定排序,时间复杂度O(nlogn),空间复杂度O(n),可用来统计逆序对个数$
7. 快速排序
非常重要的排序.
基本思想是:
1. 选一个中轴.
2. 把数组劈成两半,左边比中轴小,右边比中轴大.
3. 递归处理两边.
template <class T> void Sort(T *st,T *ed) { T k=*st; T *L=st,*R=ed; while (L<R) { while (L<R&&*R>=k) R--; if (L<R) *L=*R; while (L<R&&*L<=k) L++; if (L<R) *R=*L; } *L=k; if (st<L-1) Sort(st,L-1); if (L+1<ed) Sort(L+1,ed); }
当然这只是最裸的快速排序,复杂度最高可达到O(n2),快速排序是有很多优化的.
不稳定排序,平均时间复杂度O(nlogn),最坏时间复杂度O(n2),可用来求第k大值
8. 希尔排序
感觉没什么用.
9. 堆排序
优化了的选择排序.
虽然常数大,但是它可以维护序列顺序,这是其他排序做不到的.
template <class T,int M,bool (*f)(int &A,int &B)> struct heap { T num[M<<2]; int tot; void up(int p) { while (p!=1) { if (f(num[p],num[p>>1])) { swap(num[p],num[p>>1]); p>>=1; } else break; } } void down() { int p=1; while ((p<<1)<=tot) { int to=p<<1; if (to+1<=tot&&f(num[to+1],num[to])) to++; if (f(num[to],num[p])) swap(num[p],num[to]),p=to; else break; } } bool empty() { return tot; } T top() { return num[1]; } void push(int x) { num[++tot]=x; up(tot); } void pop() { num[1]=num[tot--]; down(); } };
不稳定排序,时间复杂度O(nlogn)
希尔还没写,留着以后填吧.