希尔排序

来源:互联网 发布:数组tostring 编辑:程序博客网 时间:2024/06/06 01:59

这里开始介绍第一种复杂排序算法——希尔排序。


我们知道插入排序在有些时候即数据本身基本有序(较小的数据基本在前面,较大的数据基本在后面)或者数据数目较少的时候,由于此时数据数目较少且基本有序,可以使得插入排序这种邻位移动的次数不会太多,所以此时效率较高。希尔排序就是基于此事实,对插入排序进行改进,简单来说,希尔排序就是高效版的插入排序,其核心思想是根据一个步长,将序列分成多个子序列(子序列的数目就是步长值,但子序列的长度不定,但与步长成反比),每个子序列中的元素都相隔一个步长,这样每个子序列的长度相比原数组序列短,然后分别对这些子序列进行插入排序,这样较小的元素尽可能地排在了前面,较大的元素尽可能地排在了后面,这样就使得原序列基本有序,完成上述步骤后就完成了一轮希尔排序,接下来缩短步长重复上述操作,由于经过一轮希尔排序后数组变的基本有序,所以下一轮希尔排序效率就会提高,直至步长为1,最终完成排序。可见一轮希尔排序就是对多个子序列的插入排序,而我们需要多轮希尔排序才能完成最终的排序,而且最后一轮希尔排序其步长必须为1,这样才能保证数组序列真正有序。


下面解析希尔排序的代码,相比原两层循环的插入排序,这里是三层嵌套for循环,最外一层是对每个子序列进行遍历,里面两层就是是对所给定的子序列进行插入排序

public static void xiEr(int[] arr,int dk){//这里得给出步长值    for(int i=0;i<dk;i++){        for(int j=1;i+j*dk<arr.length;j++){//这里i+j*dk才是数组中的相应子序列元素的角标,j则是控制相应子序列中的元素位置            for(int k=i+j*dk;k>i && arr[k]<arr[k-dk];k-=dk){//这里就是仿照插入排序的思路,注意k>i而不是k>0,另外k得自减dk                int temp=arr[k];                arr[k]=arr[k-dk];                arr[k-dk]=temp;            }        }    }}

我们可以发现希尔排序中子序列中元素的交换是跳跃式的移动,从而比传统的插入排序那种邻位移动的移动距离更大,所以效率更高。但也是因为跳跃式移动,导致它并不是稳定的排序算法。这里希尔排序的步长选择非常关键,由于涉及到数学问题,目前没有最佳方案。平时使用希尔排序步长可以依次选择5、3和1,这里注意当步长为1时,就退化成普通的插入排序了,所以说如果第一轮希尔排序就选择步长为1,那么这与普通插入排序没有区别,所以需要进行多个步长的希尔排序才能完成最终的排序。目前发现希尔排序的最优时间复杂度可以达到O(n^1.3),但最差的时间复杂度为O(n^2),其平均时间复杂度没有定值,约为O(nlogn)~O(n^2),其空间复杂度为O(1)。最后需要明确的是在记录数较少的时候,希尔排序并没有体现出优势,当记录数较多的情况下,此时希尔排序这种跳跃式移动的优势就显现出来了,人家能够更加直接简单快速地移动元素,当然复杂排序算法就是应用在这种数据数目较多的场景中。