注:本文参考https://www.cnblogs.com/chengxiao/p/6104371.html html
在讲解希尔排序以前,咱们有必要先回头看一下插入排序的问题。插入排序无论数组分布时怎么样的,都是一步步的对元素进行比较,移动,插入。好比[5,4,3,2,1,0]这种倒序序列,数组末端的0要回到首位很费劲,比较和移动元素均需n-1次。这时就引出了希尔排序。算法
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序通过改进以后的更高效的版本。该算法是突破O(n^2)的第一批算法之一。数组
希尔排序是把记录按下标的必定增量分组,对每组使用直接插入排序算法排序。随着增量逐渐减少,每组包含的数字愈来愈多,当增量减至1时,整个文件刚好被分红一组,算法便终止。dom
咱们来看下希尔排序的基本步骤,咱们选择增量gap = length/2,缩小增量继续以gap = gap/2 的方式,这种增量选择咱们能够用一个序列来表示,{n/2,(n/2)/2...1}的增量序列。性能
原始数组 如下数据元素颜色相同的为一组测试
初始增量 gap = length/2 = 5,因此整个数组被分为5组,即从第一位置的数字开始向后+n*gap为同一组,因此第一次增量后5组为[8,3],[9,5],[1,4],[7,6],[2,0]优化
对这五组分别进行插入排序,例如[8,3]插入排序后会交换位置[3,8],[9,5]插入排序后会交换位置为[5,9]等等。因此执行后会将小的数字放在数组的前面。spa
而后继续减少增量gap=5/2 = 2,数组被分为2组,即从第一位置的数字开始向后+n*gap为同一组,搜衣第二次增量后2组为[3,1,0,9,7] 和 [5,6,8,4,2]。以下图所示code
对以上两组再分别进行插入排序,在[3,1,0,9,7]中会变为[0,1,3,7,9], [5,6,8,4,2]会变为[2,4,5,6,8],以下图所示,能够看到整个数组的有序程度更进一步了。htm
再缩小增来gap=2/2 = 1 ,此时整个数组为1组[0,2,1,4,3,5,7,6,9,8],以下图所示
通过上面的“宏观调控”,整个数组已经基本有序。此时再对整个数组进行插入排序,只需进行少许的交换便可变为有序数组。
1.交换式实现方式
1 /** 2 * 交换式排序 3 * @param array 4 */ 5 public static void sort1(int[] array){ 6 int temp = 0; 7 int count = 0; 8 for(int gap=array.length/2; gap >0; gap /= 2){ 9 for (int i = gap; i < array.length; i++) { 10 int j = i - gap; 11 while (j >=0 && array[j] > array[j+gap]){ 12 //所在分组需作交换 13 temp = array[j]; 14 array[j] = array[j+gap]; 15 array[j+gap] = temp; 16 j -= gap; 17 } 18 } 19 } 20 }
2.移位式实现方式
1 /** 2 * 移位式排序 3 * @param array 4 */ 5 public static void sort2(int[] array){ 6 for(int gap = array.length/2; gap > 0; gap /= 2){ 7 for(int i=gap; i< array.length; i++) { 8 int insertValue = array[i]; 9 int insertIndex = i - gap; 10 int startIndex = insertIndex; 11 while (insertIndex >= 0 && insertValue < array[insertIndex]) { 12 //所在分组需作后移 13 array[insertIndex + gap] = array[insertIndex]; 14 insertIndex -= gap; 15 } 16 if(insertIndex != startIndex){ 17 array[insertIndex + gap] = insertValue; 18 } 19 } 20 } 21 }
1)第一层for循环用于肯定分组大小,这里选用对数组大小减半的的方式,后面每次对原分组减半,直到分组大小为1
2)第二层for循环表示从第gap个元素开始(这里说明下为何从第gap个元素开始:第gap个元素恰好为分组中的第一组的第二个元素,由于咱们的插入排序为从第二个元素开始向前比较),向后依次执行插入排序,直到最后一个元素
3)while循环用于对每一个分组进行插入排序(交换式为当找到比插入元素大的元素时,交换两个元素。移位式为当找到比插入元素大时,将那个元素向后移动gap位,最后再将待插入元素放在空缺的位置便可)
从咱们的分析可知,希尔排序中对于增量序列的选择十分重要,直接影响到希尔排序的性能。咱们上面选择的增量序列{n/2,(n/2)/2...1},其最坏时间复杂度依然位O(n^2),一些通过优化的增量序列如Hibbard通过复杂证实可以使得最坏时间复杂度为O(n3/2)。
与前面的排序算法相同,咱们依然生成10万个随机数的数组,使用插入排序方法进行排序,看其执行时间。测试代码以下
1 public static void main(String []args){ 2 int[] array = new int[1000000]; 3 for (int i = 0; i < 1000000; i++) { 4 array[i] = (int) (Math.random() * 8000000); 5 } 6 long begin = System.currentTimeMillis(); 7 sort2(array); 8 System.out.println("总耗时="+(System.currentTimeMillis()- begin)); 9 }
能够看到希尔排序算法比插入排序更快,10万个数据的数组排序大概须要300多毫秒时间。下篇咱们将介绍快速排序算法,排序效率是否会更高呢?一块儿期待!
希尔排序是一个基于插入排序的算法,解决了当待排序数组倒序时使用插入排序算法要移动屡次的耗时操做。希尔排序的核心算法为先将待排序数组进行分组进行小范围的排序,每次分组排序后待排序数组就会在大范围上更有序。当分组大小减少到1时,待排序数组已经基本有序,此时再执行插入排序,能够确保移动或交换的次数更少,从而达到提高排序效率的目的。
注:本文参考https://www.cnblogs.com/chengxiao/p/6104371.html