赞
踩
大家端午节安康,这两天都忙着玩了。没什么时间写技术博客。不过定好的计划一定要努力完成。
这次给大家介绍一下希尔排序算法,其实这是建立在插入排序之上的一种排序算法。当然,作为一个程序员,我们听到诸如:“建立在XX之上”,“是XXX的改进版”,“继XXX后,XXXX横空出世”这些句子,本能会思考旧东西的缺点,新东西的优点,新东西解决了旧东西什么问题。那么插入排序有什么问题呢?
如果仔细看过前面介绍插入排序算法的小伙伴,肯定知道插入排序是要移动元素的位置,将合适的元素插入至合适的位置。那么问题就来了,如果要对大量的数据排序,岂不是插入一次,要挪动大量的元素?插入排序在面对大量的数据时,效率十分慢。而希尔排序,就是插入排序的优化版,能在面对大量数据时也保持效率。
希尔排序(shell sort)这个排序方法又称为缩小增量排序,是1959年D·L·Shell提出来的。该方法的基本思想是:设待排序元素序列有n个元素,首先取一个整数increment(小于n)作为间隔将全部元素分为increment个子序列,所有距离为increment的元素放在同一个子序列中,在每一个子序列中分别实行直接插入排序。然后缩小间隔increment,重复上述子序列划分和排序工作。直到最后取increment=1,将所有元素放在同一个子序列中排序为止。
由于开始时,increment的取值较大,每个子序列中的元素较少,排序速度较快,到排序后期increment取值逐渐变小,子序列中元素个数逐渐增多,但由于前面工作的基础,大多数元素已经基本有序,所以排序速度仍然很快。
其实看图就比看文字更好理解:
假设我们有一个数组如下,我们第一次排序时,取间隔为4的数,那么数组便分成了下标为04,16,27,3的几组数,先对这几组分别排序。排好之后,我们再取间隔为2的数,那么数组分成了,0246与1357两组数,最后我们取间隔为1的数,这相当于一次插入排序。
注意,我们在一开始取间隔取得很大,目的就是做元素之间的快速移动,避免长距离的移动元素,随着后面间隔越来越小,但因为长距离间隔的元素已经是有序的了,所以不会存在下标第99999的需要插入到下标为2的元素位置上。
灰常遗憾,希尔的时间复杂度至今仍然不确定,因为对应不通的间隔,时间复杂度是不一样的
希尔排序的复杂度和增量序列是相关的:
{1,2,4,8,…}这种序列并不是很好的增量序列,使用这个增量序列的时间复杂度(最坏情形)是O(n^2)
Hibbard提出了另一个增量序列{1,3,7,…,2^k-1},
这种序列的时间复杂度(最坏情形)为O(n^1.5)
Sedgewick提出了几种增量序列,其最坏情形运行时间为O(n^1.3),其中最好的一个序列是{1,5,19,41,109,…}
/**
* 希尔排序
* @param arr 目标序列
*/
public static void shellSort(int[] arr){
int len = arr.length;
for(int gap=len/2; gap>=1; gap=gap/2){ //拆分整个序列,元素间距为gap(也就是增量)
//对子序列进行直接插入排序
for(int i=gap+1; i<len; i++){
for(int j=i-gap; j>=0&&arr[j]>arr[j+gap]; j=j-gap){
swap(arr,j,j+gap);
}
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。