排序算法——希尔排序(shell)
希尔排序是建立在插入排序上的优化
我们上一篇已经知道了插入排序在面对已经有序的情况下会更加快一些,
所以希尔排序的思想在于先让整体有序,然后再进行最后一次插入排序,让最后一次插入排序更加快。
我现在先举夸张例子
有一个数组:10,9,8,7,6,5,2,3,4,1,我们如果直接使用插入排序也可以,但是效率一般
但是我们可以在插入排序之前进行整体优化,让某个数字与跨越n位下标的数字比较,越过中间的比较直接换位。比如a[n]+a[n+h]。这样的粗略比较排序往往会让数组更加有序便于最后一次排序的操作更加便利
比如我们取优化排序间隔为5则
原数组:10,9,8,7,6,1,2,3,4,5
优化后:5,2,3,4,,1,10,9,8,7,6
这样的数组小的这一部分就会在前面,大的这一部分就会在后面,再进行最后一步的插入排序就能节约很多比较交换的时间。
然后我们再带入递归思想在操作大数组时,比如1000位数组我们可以先以625为间隔进行插入排序,再用125为间隔,再用25为间隔……一直到1为间隔。
这样不断的分层优化下希尔排序会比插入排序更加快一些。
但是若原数组是相对有序状态:比如已经排列好的数组只是部分数字改动需要重新排序,那么我们的希尔排序就多做了很多无用功了。
我们希尔排序只对于混乱程度高的数组有优势而已。
希尔排序还受到排序因子的影响。也就是我们的间隔。
复杂度:
因为受到影响非常多,比如长度,比如排序状态,比如因子,因此希尔排序是一个很难分析的复杂度:普遍认为时间复杂度在:N1.3~N2。在面对大型数组的时候效率可能稍微低点,在面对中型数组的时候效率不错。再加上空间复杂度为O(1)。因此表现相对良好我们也常用。
编写思路(伪代码):
1创建因子变量,数组大小变量,将因子等比扩大到小于数组大小状态
2做出一个while循环不断等比缩小因子
3在循环内部以这个因子为距离进行插入排序
以下为代码
/** * 希尔排序 * @author DHH * */ public class Select extends Example { /** * 希尔排序 */ public void sort(Comparable[] a){ int h=1; int n=a.length; //扩大因子 while(h<n/3)h*=3; //缩小因子的循环 while(h>=1){ //以因子为距离的插入排序 for(int i=0;i<n;i++){ for(int j=i;j-h>=0&&compare(a[j],a[j-h])>0;j-=h){ exch(a,j,j-h); } } h/=3; } } /** * 测试方法 * @param args */ public static void main(String[] args) { DATA[] dataArray=new DATA[15]; dataArray[0]=new DATA(16); dataArray[1]=new DATA(6); dataArray[2]=new DATA(15); dataArray[3]=new DATA(8); dataArray[4]=new DATA(11); dataArray[5]=new DATA(23); dataArray[6]=new DATA(75); dataArray[7]=new DATA(4); dataArray[8]=new DATA(11); dataArray[9]=new DATA(60); dataArray[10]=new DATA(71); dataArray[11]=new DATA(24); dataArray[12]=new DATA(56); dataArray[13]=new DATA(31); dataArray[14]=new DATA(42); System.out.println(Arrays.toString(dataArray)); Select select=new Select(); select.sort(dataArray); System.out.println(Arrays.toString(dataArray)); } }