10种排序算法
一,冒泡排序
#include <bits/stdc++.h> #define ll long long using namespace std; int main(){ int n;scanf("%d",&n); int a[100]; for(int i=0;i<n;i++){ scanf("%d",&a[i]); } int x,numx=0; //x为一趟排序经行进行的交换次数 //numx为总的交换次数,numx也为逆序对对数 for(int i=n;i>0;i--){ x=0; for(int j=1;j<n;j++){ if(a[j-1]>a[j]){ swap(a[j-1],a[j]); x++; } } numx+=x; } for(int i=0;i<n;i++){ printf("%d ",a[i]); }printf("\n"); printf("%d\n",numx); return 0; }
特点:1,交换次数为逆序对对数。2,对数组只经行一种操作,即交换两相邻元素,所以用链表实现冒泡排序复杂度不会改变。3,在遍历时如果发现交换次数为0,说明以及排序完成。
二,选择排序
#include <bits/stdc++.h> #define ll long long using namespace std; int main(){ int n;scanf("%d",&n); int a[100]; for(int i=0;i<n;i++){ scanf("%d",&a[i]); } for(int i=n;i>0;i--){ int x=0; for(int j=1;j<n;j++){ if(a[x]>a[j]){ swap(a[x],a[j]); x=j; } } } for(int i=0;i<n;i++){ printf("%d ",a[i]); }printf("\n"); return 0; }
特点:1,交换次数最多为n-1次。
三,插入排序
#include <bits/stdc++.h> #define ll long long using namespace std; int lower_bound_(int *A,int x,int y,int v){ int m; while(x<y){ m=x+(y-x)/2; if(A[m]>=v)y=m; else x=m+1; } return x; } int main(){ int n;scanf("%d",&n); int a[100]; for(int i=0;i<n;i++){ scanf("%d",&a[i]); } for(int i=1;i<n;i++){ int x=lower_bound_(a,0,i,a[i]); if(x==i)continue; int y=a[i]; for(int j=i;j>x;j--){ a[j]=a[j-1]; } a[x]=y; } for(int i=0;i<n;i++){ printf("%d ",a[i]); }printf("\n"); return 0; }
特点:1,使用二分,但因为要平移数组所以二分操作并不会降低时间复杂度。2,每次都先讲待排部分先与已经排序好的部分的最大值经行比较,加入这个操作后,对已经排序好的数组经行插入排序时间复杂度为O(n)。3,在处理比较有序的数组时,插入排序会表现出比较高的效率。
四,希尔排序(插入排序+同余系)
#include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+1; long long cnt; int l; int a[maxn]; int n; vector<int >G; //指定间隔g的篡改人排序 void insertionSort(int a[],int n,int g){ for(int i=g;i<n;i++){ int v=a[i]; int j=i-g; while(j>=0&&a[j]>v){ a[j+g]=a[j]; j-=g; cnt++; } a[j+g]=v; } } void shellSort(int a[],int n){ //生成数列1,4,13,40,121,364,1093 for(int o=1;;){ if(o>n)break; G.push_back(o); o=3*o+1; } for(int i=G.size()-1;i>=0;i--){//逆序指定g insertionSort(a,n,G[i]); } } int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } cnt=0; shellSort(a,n); for(int i=G.size()-1;i>=0;i--){ printf("%d ",G[i]); } printf("\n"); for(int i=0;i<n;i++){ printf("%d ",a[i]); } printf("\n"); return 0; }
特点:1.想完成数据排序必须再最后执行g=1,即普通的插入排序法。2.G数组的选择方法有很多,当G为1,4,13,40,121,等即gn+1=gn*3+1时,复杂度基本维持再O(N1.25)。3.遇到2的幂指数,再g=1之前几乎不需要排列的数组,希尔排序的效率大打折扣。
五,归并排序
#include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+10; int nxd; void merge_sort(int *a,int l,int r,int *s){ if(r-l>1){ int mid=(l+r)>>1; merge_sort(a,l,mid,s); merge_sort(a,mid,r,s); int p=l,q=mid,o=l; while(p<mid||q<r){ if(q>=r||(p<mid&&a[p]<=a[q]))s[o++]=a[p++]; else s[o++]=a[q++],nxd+=mid-p; } for(o=l;o<r;o++){ a[o]=s[o]; } } } int main(){ int a[maxn]; int s[maxn];//辅助数组 int n; nxd=0; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } merge_sort(a,0,n,s); printf("%d\n",nxd); return 0; }
特点:1,复杂度稳定为O(nlogn)。2,可以快速计算出逆序对对数。
六,堆排序
#include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+10; int heap[maxn],sz=0; int n; int a[maxn]; void push(int x){ int i=sz++; while(i>0){ int p=(i-1)/2; if(heap[p]<=x)break; heap[i]=heap[p]; i=p; } heap[i]=x; } int pop(){ int ret=heap[0]; int x=heap[--sz]; int i=0; heap[sz]=ret; while(i*2+1<sz){ int a=i*2+1; int b=i*2+2; if(b<sz&&heap[b]<heap[a])a=b; if(heap[a]>=x)break; heap[i]=heap[a]; i=a; } heap[i]=x; return ret; } int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); push(a[i]); } for(int i=0;i<n;i++){ a[i]=pop(); } for(int i=0;i<n;i++){ printf("%d ",a[i]); } printf("\n"); return 0; }
特点:1,可以快速计算出前k大的元素。
七,快速排序
#include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+10; int num[maxn]; void quicksort(int l,int r){ int temp,t,i,j; if(l>=r)return; temp=num[l]; i=l; j=r; while(i<j){ while(num[j]>temp&&i<j){ j--; } while(num[i]<=temp&&i<j){ i++; } if(i<j){ t=num[j]; num[j]=num[i]; num[i]=t; } } num[l]=num[i]; num[i]=temp; quicksort(l,i-1); quicksort(i+1,r); return; } int main(){ int i,n; scanf("%d",&n); for(i=0;i<n;i++){ scanf("%d",&num[i]); } quicksort(0,n-1); for(i=0;i<n;i++){ printf("%d ",num[i]); } return 0; }
特点:1,效率和原数组顺序有关,随机打乱原数组或每次取随机位置的分割值可减小原数组对这种算法的影响。
八,计数排序
#include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+1; int n; int A[maxn]; int T[maxn];//辅助数组。 int main(){ scanf("%d",&n); int k=0; //数组的最大数 for(int i=0;i<n;i++){ scanf("%d",&A[i]); T[A[i]]++; if(A[i]>k)k=A[i]; } for(int i=0,j=0;i<=k;i++){ while(T[i]>0){ A[j++]=i; T[i]--; } } for(int i=0;i<n;i++){ printf("%d ",A[i]); } printf("\n"); return 0; }
特点:1,效率和数据大小有关
九,桶排序
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <vector> #include <map> using namespace std; const int maxn=1e5+10; const int T=100;//桶个数 int main(){ vector<int>t[T]; int n; int a[maxn]; int mina,maxa; //数组的最小和最大元素 int ch; //桶大小 scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); if(i==0){ mina=maxa=a[i]; } else{ mina=min(mina,a[i]); maxa=max(maxa,a[i]); } } maxa++; if(maxa-mina>T) ch=(maxa-mina+T-1)/T; else ch=1; //退化成了计数排序 for(int i=0;i<n;i++){ //放入对应的桶中 t[(a[i]-mina)/ch].push_back(a[i]); } for(int i=0;i<T;i++){ //桶内排序 sort(t[i].begin(),t[i].end()); } int x=0; for(int i=0;i<T;i++){ for(int j=0;j<t[i].size();j++){ a[x++]=t[i][j]; } } for(int i=0;i<n;i++){ printf("%d ",a[i]); } return 0; }
特点:1,如果桶的大小为1,那么桶排序退化为计数排序。2,桶排序采用的是分块的思想,对于桶内,可以采取任意一种排序方式。
十,基数排序
基数排序有两种思路,一种为先高位比较,次低位比较,这样就相当于递归做桶排序,即桶排序的每一个桶里继续做桶排序,直到退化为基数排序。
另外一种为先低位比较再高位比较,过程如下
基数排序使对桶排序的一种优化,因为桶排序极不稳定,出现a[]={1,401,402,403,440,405}这种数据因为分桶的不合理时间复杂度退化到常规排序的时间
于是牛人就想到了由低到高根据每一位上的数字分桶,比如上面提到的数据,由于最大数字使3位,所以要进行三次分桶。
第一次分桶,根据最低位分桶
0->440
1->1->401
2->402
3->403
4
5->405
6
7
8
9
第一次分桶完得到的数组:440,1,401,402,403,405
第二次根据十位数字分桶:
0->1->401->402->403->405
1
2
3
4->440
5
6
7
8
9
第二次分桶的结果1,401,402,403,405,440(数组已经有序0.0)
第三次根据百位数字分桶:
0->1
1
2
3
4->401->402->403->405->440
5
6
7
8
9
排序结果:1,401,402,403,405,440
//先低位再高位 #include <algorithm> #include <iostream> #include <cstring> #include <vector> #include <cstdio> #include <cmath> #include <queue> using namespace std; const int maxn=1e5+10; int maxwei(int a[],int n){ int maxx=0; for(int i=0;i<n;i++){ int count=1,tem=a[i]; while(tem/10!=0){ tem=tem/10; count++; } if(count>maxx) maxx=count; } return maxx; } void basesort(int a[],int n){ int maxx=maxwei(a,n); //取得最大位数 int num=1; for(int i=0;i<maxx;i++){ int count[10]; //声明count为了统计每个桶放了几个数 int temp[10][1000]; //temp相当于桶,前一个数标记第几个篮子,后一个为了标记放的个数 for(int f=0;f<10;f++){ count[f]=0; } for(int g=0;g<10;g++){ for(int z=0;z<n;z++){ temp[g][z]=0; } } for(int j=0;j<n;j++){ int fg=a[j]/num; int k=fg%10; count[k]++; int te=count[k]-1; temp[k][te]=a[j]; } int b=0; for(int h=0;h<10;h++){ if(count[h]>0){ for(int v=0;v<count[h];v++){ a[b]=temp[h][v]; b++; } } } num=num*10; } } void print(int a[],int n){ for(int i=0;i<n;i++){ printf("%d ",a[i]); } printf("\n"); } int main(){ int a[maxn]; int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } basesort(a,n); print(a,n); return 0; }