常用排序算法总结(插入、冒泡、快速,堆、选择、希尔、归并)

来源:互联网 发布:mac终端更改系统时间 编辑:程序博客网 时间:2024/05/22 05:12

对科研中常见的几种排序方法做个总结,方便日后查看与回顾。(对本人认为比较简单易懂的排序方法过程比较简略,对比较复杂的排序方法过程就比较详细)

编程语言:C++(经过本人验证,可以正常运行)。

一、直接插入排序(稳定):

思想:“一趟一个“地将待排序记录插入到已经排好序的部分记录的适当位置中,使其成为一个新的有序序列,直到所有待排序记录全部插入完毕。

分析:

空间:仅需使用一个辅助单元。故空间复杂度为O(1);

最好情况:待排序序列已经有序,每趟操作只需比较1次和移动0次,此时,总的比较次数为n-1,总移动次数为0,故最好情况算法复杂度为O(n);

最坏情况:待排序序列按逆序排序,这时在第j趟操作中,为插入元素需要同前面的j个元素比较,移动元素的次数为j+1.此时有:总的比较次数=1+2+...+(n-1)=n(n-1)/2;总移动次数=2+3+4+...+n=(n+2)(n-1)/2,故最坏情况算法复杂度为O(n^2);

平均情况:在第j趟操作中,插入记录大约需要同前面j/2个元素比较,移动记录的次数为j/2+1次,此时总的比较次数约为n^2/4;总移动次数为n^2/4;故时间复杂度为O(n^2);

代码:

void insertSort(int *a,int low,int high)//插入排序 {for(int i=low+1;i<high;i++){if(a[i]<a[i-1]){int tmp=a[i];a[i]=a[i-1];int j=i-2;for(;j>=low&&tmp<a[j];j--)a[j+1]=a[j];a[j+1]=tmp;}}}
二、二分插入排序(稳定):

思想:与直接插入类似,在找寻插入位置时采用二分查找方式;

分析:与直接插入相同,仅仅减少了元素的比较次数,并没减少元素的移动次数,因此仍为O(n^2);

代码:

void binaryInsertSort(int *a,int low,int high)//二分插入排序 {for(int i=low+1;i<high;i++){int tmp=a[i];int hi=i-1;int lo=low;while(lo<=hi){int mid=(lo+hi)/2;if(a[mid]>tmp) hi=mid-1;else lo=mid+1;}for(int j=i-1;j>hi;j--){a[j+1]=a[j];}a[hi+1]=tmp;}}
三、希尔排序(不稳定):

思想:”基本有序化“可以提升插入排序的效率,将记录序列分成若干子序列,每个子序列分别进行插入排序。需要注意的是,这种子序列是由间隔为某个增量的一组数据组成,即跳跃式选择数据组。(这个解释起来比较抽象,面试中也很少问,但编程贼简单)

分析:时间复杂度:开始时增量较大,分组较多,每组的记录数目较少,当排序规模n值较小时,n和n^2的差距也较小,但随着增量d逐渐缩小,分组逐渐减小,而各组的记录数目逐渐增多,但由于已经基本有序,所以新的一趟也较快。时间复杂度与增量的设置有关,约在O(n^1.25)和O(n^1.6)之间;

代码:

void shellInsert(int *a,int low,int high,int deltaK)//希尔排序 {for(int i=low+deltaK;i<high;i++){if(a[i]<a[i-deltaK]){int tmp=a[i];int j=i-deltaK;for(;j>=low&&tmp<a[j];j=j-deltaK)a[j+deltaK]=a[j];a[j+deltaK]=tmp;}}} void shellSort(int *a,int low,int high,int *delta,int m)//希尔排序 {for(int k=0;k<m;k++){shellInsert(a,low,high,delta[k]);}}
四、冒泡排序(稳定):

思想:贼简单,对n个数进行n-1次扫描,每次扫描比较相邻两个数字,大的移到后面。这样每趟排序会把最大的数字扔到最后面;

分析:

空间:需要一个辅助单元,故空间复杂度为O(1);

最好情况:数据全部排好序,这时循环n-1次,时间复杂度为O(n),但是程序中可以设置flag,某一次未发生位置交换,程序退出;

最坏情况:数据全部逆序存放,这时循环n-1次,比较次数为n-1+n-2+...+1=n(n-1)/2;移动次数为3*(n-1+n-2+...+1)=3n(n-1)/2,时间复杂度为O(n^2);

平均情况:与直接插入分析一样,时间复杂度为O(n^2);

代码:

void bubbleSort(int *a,int low,int high)//冒泡排序普通实现 {int n=high-low;for(int i=0;i<n-1;i++){int flag=0;for(int j=0;j<n-i-1;j++){if(a[j]>a[j+1]){flag=1;int tmp=a[j];a[j]=a[j+1];a[j+1]=tmp;}}if(flag==0)break;}}
这里有个小插曲,有一次腾讯面试,让用递归实现冒泡,瞬间懵逼。

代码(冒泡排序的递归实现):

void bubbleSort2(int *a,int low,int high)//冒泡排序递归实现 {if(high==1)return;for(int i=0;i<high-1;i++){if(a[i]>a[i+1]){int tmp=a[i];a[i]=a[i+1];a[i+1]=tmp;}}bubbleSort2(a,low,--high);} 
五、快速排序(不稳定):

思想:每次选择一个元素放置到它应该在的位置,划分原来序列为前面的数都不大于它,后面的数都不小于它;

分析(消耗的时间空间主要在递归上):

空间:最好情况,每次取到的元素刚好平分整个数组,此时空间复杂度为O(log n);最坏情况,每次取到的就是数组的最大或最小的,这种情况就是冒泡排序,此时空间复杂度为O(n),所以空间复杂度为O(log n)~O(n);

最好情况:相当于一个完全二叉树结构,即每次标准元素都把当前数组分成两个大小相等的子数组,这是分解次数等于完全二叉树的深度lb n;每次快排过程无论把数组怎样划分,全部的比较次数都接近于n-1次,所以时间复杂度为O(n lb n);

最坏情况:退化成冒泡排序,时间复杂度为O(n^2);

平均情况:时间复杂度为O(n lb n);

代码:

void quickSort(int *a,int low,int high)//快速排序 {int i=low;int j=high-1;int tmp=a[low];while(i<j){while(i<j&&tmp<=a[j]) j--;if(i<j){a[i]=a[j];i++;}while(i<j&&tmp>=a[i]) i++;if(i<j){a[j]=a[i];j--;}}a[i]=tmp;if(low<i) quickSort(a,low,i-1);if(i<high) quickSort(a,i+1,high);}
六、直接选择排序(不稳定):

思想:巨简单。在每趟过程中将待排序数组最小数放到最前面。不多解释了。

分析:

空间:需要一个辅助单元,故O(1);

最好最坏情况:无论初始状态如何,都需要进行n-1+n-1+...+1=n(n-1)/2次比较,所以时间复杂度为O(n^2);

代码:

void selectSort(int *a,int low,int high)//直接选择排序 {int n=high-low;for(int i=0;i<n-1;i++){int min=i;for(int j=i+1;j<n;j++){if(a[min]>a[j])min=j;}if(min!=i){int tmp=a[min];a[min]=a[i];a[i]=tmp;}} }
七、堆排序(不稳定):

思想:把待排序的数据元素构造成一个完全二叉树结构,则每次选择出一个最大的数据元素只需比较完全二叉树的高度次,即lb n次,则排序算法的最好最坏时间复杂度均为O(n lb n);

空间:O(1);

这个比较复杂,具体的讲解可以参考博文:http://www.cnblogs.com/skywang12345/p/3602162.html

代码:

void heapAdjust(int *a,int low,int high)//堆排序,调整堆 {int tmp=a[low];for(int j=2*low+1;j<high;j=j*2+1){if(j<high-1&&a[j]<a[j+1]) j++;if(tmp>=a[j]) break;a[low]=a[j];a[j]=tmp;low=j;}//a[low]=tmp;}void heapSort(int *a,int high)//堆排序 {for(int i=(high)/2-1;i>=0;i--)heapAdjust(a,i,high);for(int i=high-1;i>0;i--){int tmp=a[0];a[0]=a[i];a[i]=tmp;heapAdjust(a,0,i);}} 
八、归并排序(稳定):

思想:将若干个已经排好序的子序列合并成一个有序的序列。

分析:

时间:归并的次数约为lb n,任何一次的归并比较次数约为n-1,所以最好最坏时间复杂度均为O(n lb n);
空间:需要一个与原数组大小一样的辅助数组,故空间复杂度为O(n).

代码:

void merge(int *a,int p,int q,int r)//一次归并算法 {int n1=q-p+1;int n2=r-q;int *L=new int[n1+1];int *R=new int[n2+1];for(int i=0;i<n1;i++){L[i]=a[p+i];}for(int j=0;j<n2;j++){R[j]=a[q+j+1];}L[n1]=1000000;R[n2]=1000000;for(int i=0,j=0,k=p;k<=r;k++){if(L[i]<=R[j]){a[k]=L[i];i++;}else{a[k]=R[j];j++;}}delete []L;delete []R;}void mergepass(int *a,int p,int r)//二路归并 {if(p<r){int q=(p+r)/2;mergepass(a,p,q);mergepass(a,q+1,r);merge(a,p,q,r);}} 


总结:具体的算法过程以后有时间会补上哒~但面试前看看这些还是挺有效果的呢!

最后补上一张各种排序算法的对比:















阅读全文
0 0
原创粉丝点击