排序算法总结之希尔排序

一,希尔排序算法介绍

①希尔排序又称缩小增量排序 ,它本质上是一个插入排序算法。为什么呢?

因为,对于插入排序而言,插入排序是将当前待排序的元素与前面所有的元素比较,而希尔排序是将当前元素与前面增量位置上的元素进行比较,然后,再将该元素插入到合适位置。当一趟希尔排序完成后,处于增量位置上的元素是有序的。

②希尔排序算法的效率依赖于增量的选取

假设增量序列为 h(1),h(2).....h(k),其中h(1)必须为1,且h(1)<h(2)<...h(k) 。

第一趟排序时在增量为h(k)的各个元素上进行比较;

第二趟排序在增量为h(k-1)的各个元素上进行比较;

..........

最后一趟在增量h(1)上进行比较。由此可以看出,每进行一趟排序,增量是一个不断减少的过程,因此称之为缩小增量。

当增量减少到h(1)=1时,这里完全就是插入排序了,而在此时,整个元素经过前面的 k-1 趟排序后,已经变得基本有序了,而我们知道,对于插入排序而言,当待排序的数组基本有序时,插入排序的效率是非常高的。因此,希尔排序就是利用“增量”技巧将插入排序的平均时间复杂度O(N^2)降低为亚二次方。

 

二,希尔排序实例分析

假设原始数组为: 81   94   11   96   12   35    增量序列为 h(1)=1  h(2)=3

这里一共有两个增量序列,故一共有两趟排序。第一趟按照增量3来排序

处于增量3上的元素集合如下:<81      96>,<94      12>,<11      35>排序之后变成:<81      96>,<12      94>,<11      35>

即,数组变成了:81   12   11   96   94   35

可以看出,在增量为3的各个位置上的元素都是有序的。

经过前面一趟排序,此时数组已经基本有序,再使用增量为1的排序时(插入排序),比较的次数将会大大地降低。

第二趟按增量为h(1)=1 来排序,排序后数组变成:

11   12   35   81   94   96

 

三,算法实现

 1 public class ShellSort {
 2     
 3     /**
 4      * 使用希尔推荐的增量: h(k)=N/2 , h(i)=h(i+1)/2
 5      * @param arr 待排序数组
 6      */
 7     public static <T extends Comparable<? super T>> void shellSort(T[] arr){
 8         
 9         int j;
10         for(int gap = arr.length; gap >= 1; gap /=2)//排序的趟数
11         {
12         
13             /*每对增量序列为:
14              * <gap, 0> <gap+1,1> <gap+2,2>.....<arr.length-1, arr.length-1-gap>
15              */
16             for(int i = gap; i < arr.length; i++)//在"增量位置上" 进行插入排序
17             {
18                 T tmp = arr[i];
19                 for(j = i; j >= gap && tmp.compareTo(arr[j - gap]) < 0; j-= gap)
20                     arr[j] = arr[j - gap];
21                 arr[j] = tmp;
22             }
23         }
24     }
25     
26     //for test purpose
27     public static void main(String[] args) {
28         Integer[] arr = {81,94,11,96,12,35,17,95,28,58,41,75,15};
29         shellSort(arr);
30         for (Integer integer : arr) {
31             System.out.print(integer + " ");
32         }
33     }
34 }

①第10行for循环表示,一共有多少个增量。增量的序列的个数,就是希尔排序的趟数。

上面的增量序列为: arr.length/2 , arr.length/2/2, arr.length/2/2/2    ....   2,  1

②里层的两个for循环(第16行至23行)实际上是一个插入排序

③第16行for循环表示:从数组的第 arr.length/2 下标起,对各组增量序列上的元素进行插入排序。gap等于arr.length/2时的排序分析如下:

对于第一趟排序而言(gap等于arr.length/2),一共有多少组呢?答案是一共有 arr.length - arr.length/2 + 1 组。

即:arr[arr.length/2]   arr[0] 这两个元素是一组

arr[arr.length/2 + 1]   arr[1] 这两个元素是一组

.....

arr[arr.length -1]   arr[arr.length-1-gap]这两个元素是一组,此时gap等于arr.length/2

④第19行for循环,就是插入排序中的将当前元素与前面的元素进行比较。这里的前面元素是相差 gap 个位置上的元素。

比如, arr[length/2]  与 arr[length/2-gap]进行比较。

 

四:参考资料

排序算法总结之快速排序

排序算法总结之归并排序

排序算法总结之堆排序

排序算法总结之插入排序

各种排序算法的总结

 

至此,五大基于内存的排序算法:插入排序、希尔排序;归并排序、快速排序;堆排序 全部已经实现了一遍。

插入排序和希尔排序是一类,本质上希尔排序就是插入排序。插入排序它的增量就是1,而希尔排序则按多个增量进行排序,增量逐渐减少至1

归并排序和快速排序很像。之所以像,是因为它们都基于分治、递归。归并排序每次将原数组递归分成两个大小相等的子数组,而快速排序则是基于pivot元素将原数组分成两个子数组。归并排序不断地分解原数组,直到划分的两个子数组中只存在一个元素时,这时,这两个子数组可以视为都是有序的。从而,再合并两个有序的子数组,来实现排序。

堆排序,借助了二叉堆,逻辑上是一棵完全二叉树,物理存储结构是一个一维数组。通过不断地删除堆顶元素,来实现排序。

 

 

posted @ 2016-05-28 21:50  大熊猫同学  阅读(6626)  评论(1编辑  收藏  举报