排序精讲
第一节 排序概论
1.1 排序定义
排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。分内部排序和外部排序。若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。反之,若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。内部排序的过程是一个逐步扩大记录的有序序列长度的过程。
1.2 排序分类
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。
1.3 排序算法
已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。首先比较a[1]与a[2]的值,若a[1]大于a[2]则交换两者的值,否则不变。再比较a[2]与a[3]的值,若a[2]大于a[3]则交换两者的值,否则不变。再比较a[3]与a[4],以此类推,最后比较a[n-1]与a[n]的值。这样处理一轮后,a[n]的值一定是这组数据中最大的。再对a[1]~a[n-1]以相同方法处理一轮,则a[n-1]的值一定是a[1]~a[n-1]中最大的。再对a[1]~a[n-2]以相同方法处理一轮,以此类推。共处理n-1轮后a[1]、a[2]、……a[n]就以升序排列了。降序排列与升序排列相类似,若a[1]小于a[2]则交换两者的值,否则不变,后面以此类推。 总的来讲,每一轮排序后最大(或最小)的数将移动到数据序列的最后,理论上总共要进行n(n-1)/2次交换。实际,当一次排序过后没有发生交换,那么就算作排序成功,即可退出程序。
1 2 3 4 5 6 7 8 9 10 11 12 | /*冒泡排序(改进后)*/ void BubbleSort( int a[], int n) { while (n>0) { int k; k=n-1; n=0; for ( int i=1;i<=k;i++) if (a[i]>a[i+1]) { swap(a[i+1],a[i]); n=i; } } } |
【1.3.2 选择排序】
1 2 3 4 5 6 7 8 9 | /*选择排序*/ void SelectSort( int a[], int n) { for ( int i=1;i<n;i++) { int k;k=i; for ( int j=i+1;j<=n;j++) if (a[j]<a[k]) k=j; if (k!=i) swap(a[i],a[k]); } } |
【1.3.3 插入排序】
插入排序:已知一组升序排列数据a[1]、a[2]、……a[n],一组无序数据b[1]、b[2]、……b[m],需将二者合并成一个升序数列。首先比较b[1]与a[1]的值,若b[1]大于a[1],则跳过,比较b[1]与a[2]的值,若b[1]仍然大于a[2],则继续跳过,直到b[1]小于a数组中某一数据a[x],则将a[x]~a[n]分别向后移动一位,将b[1]插入到原来a[x]的位置这就完成了b[1]的插入。b[2]~b[m]用相同方法插入。(若无数组a,可将b[1]当作n=1的数组a)
1 2 3 4 5 6 7 8 9 | /*插入排序*/ void InsertSort( int a[], int n) { for ( int i=2;i<=n;i++) { int tmp,j; tmp=a[i];j=i-1; while (a[j]>tmp) {a[j+1]=a[j];j--;} a[j+1]=tmp; } } |
【1.3.4 希尔排序】
由希尔在1959年提出。已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。发现当n不大时,插入排序的效果很好。首先取一增量d(d<n),将a[1]、a[1+d]、a[1+2d]……列为第一组,a[2]、a[2+d]、a[2+2d]……列为第二组,a[d]、a[2d]、a[3d]……列为最后一组以次类推,在各组内用插入排序,然后取d'<d,重复上述操作,直到d=1。
1 2 3 4 5 6 7 8 9 | /*希尔排序*/ void ShellSort( int a[], int n) { for ( int i=n/2;i>0;i/=2) for ( int j=i+1;j<=n;j++) { int key=a[j],k; for (k=j-i;k>=0&&a[k]>key;k-=i) a[k+i]=a[k]; a[k+i]=key; } } |
【1.3.5 快速排序】
快速排序是目前已知的常用排序算法中最快的排序方法。已知一组无序数据a[1]、a[2]、……a[n],需将其按升序排列。首先任取数据a[x]作为基准。比较a[x]与其它数据并排序,使a[x]排在数据的第k位,并且使a[1]~a[k-1]中的每一个数据<a[x],a[k+1]~a[n]中的每一个数据>a[x],然后采用分治的策略分别对a[1]~a[k-1]和a[k+1]~a[n]两组数据进行快速排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /*快速排序*/ void QuickSort( int a[], int l, int r) { int i,j,mid; i=l;j=r; mid=a[(l+r)/2]; while (i<=j) { while (a[i]<mid) i++; while (a[j]>mid) j--; if (i<=j) { swap(a[i],a[j]); i++;j--; } } if (l>j) QuickSort(a,l,j); if (i<r) QuickSort(a,i,r); } |
快速排序在C++中也有现成的,要用到STL模板。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <stdlib.h> /*这是升序*/ int cmpup( const void *x, const void *y) { return *( int *) x - *( int *) y; } /**********************************/ /*这是降序*/ int cmpdown( const void *x, const void *y) { return *( int *) y - *( int *) x; } /**********************************/ void Qsort( int a[], int n) { qsort (a,n, sizeof ( int ),cmpup); //如果是降序,那么就是: qsort (a,n, sizeof ( int ),cmpdown); } |
【1.3.6 桶排序】
已知一组无序正整数数据a[1]、a[2]、……a[n],需将其按升序排列。首先定义一个数组x[m],且m>=a[1]、a[2]、……a[n],接着循环n次,每次x[a]++.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> using namespace std; int b[101],k,n; int main() { cin>>n; for ( int i=0;i<=100;i++) b[i]=0; for ( int i=1;i<=n;i++) { cin>>k; b[k]=b[k]+1; } for ( int i=0;i<=100;i++) while (b[i]>0) { cout<<i<< ' ' ; b[i]--; } return 0; } |
【1.3.7 归并排序】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <iostream> using namespace std; int n,m,s[101],r[101],a[101]; int MergeSort() { int i=0,j=0,k=0; while ((i<n)&&(j<m)) { if (s[i]<=r[j]) {a[k]=s[i];i++;k++;} else {a[k]=r[j];j++;k++;} while (i<n) {a[k]=s[i];i++;k++;} while (j<m) {a[k]=r[j];j++;k++;} } } int main() { cin>>n; for ( int x=0;x<n;x++) cin>>s[x]; cin>>m; for ( int x=0;x<m;x++) cin>>r[x]; MergeSort(); for ( int x=0;x<n+m;x++) cout<<a[x]<< ' ' ; cout<<endl; return 0; } |
【1.3.8 堆排序】
堆排序的要素就是让所有的左子树都比根及右子树大,但不太稳定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <iostream> using namespace std; int a[1000001],n; void EditHeap( int i, int s) { int j; if (2*i<=s) { a[0]=a[i];j=i; if (a[2*i]>a[0]) {a[0]=a[2*i];j=2*i;} if ((2*i+1<=s)&&(a[2*i+1]>a[0])) {a[0]=a[2*i+1];j=2*i+1;} if (j!=i) {a[j]=a[i];a[i]=a[0];EditHeap(j,s);} } } void BuildHeap() { int i,j; for (i=n/2;i>=1;i--) { a[0]=a[i];j=i; if (a[2*i]>a[0]) {a[0]=a[2*i];j=2*i;} if ((2*i+1<=n)&&(a[2*i+1]>a[0])) {a[0]=a[2*i+1];j=2*i+1;} if (j!=i) {a[j]=a[i];a[i]=a[0];EditHeap(j,n);} } } void HeapSort() { BuildHeap(); for ( int i=n;i>=2;i--) { a[0]=a[i]; a[i]=a[1]; a[1]=a[0]; EditHeap(1,i-1); } } int main() { cin>>n; for ( int i=1;i<=n;i++) cin>>a[i]; HeapSort(); for ( int i=1;i<=n;i++) cout<<a[i]<< ' ' ; cout<<endl; return 0; } |
【1.3.9 sort排序】
sort排序是指使用C++STL算法库中的sort函数进行任意类型数据排序。
1 2 3 4 5 | #include <algorithm> int sortsort( int a[], int n) { sort(a,a+n); } |
【1.3.10 基数排序】
基数排序(radix sort)则是属于“分配式排序”(distribution sort),基数排序法又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。也称“桶排序”,与1.3.6基本相同。
这里给出网上下载的基数排序的截图:
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步