希尔排序(实现+总结)

希尔排序的名称来源于它的发明者,该算法是第一批冲破二次时间屏障的算法之一,它是基于插入排序改进而成的的一种快速的算法。

1、基本思想
  它的工作原理是比较相隔一定距离的元素,并且每趟比较所用的距离随算法进行而减小,因此,希尔排序也叫做缩减增量排序
  对于大规模的乱序数组插入排序很慢,因为它只会交换相邻元素,因此只能一点点地挪到相应位置,极端情况如最小元素刚好在数组的最右端,那它要经过n-1次移动才能到正确位置,希尔排序为了加快速度,采用交换不相邻元素的方法对数组的一部分进行排序,令数组变成插入排序喜欢的部分有序状态,最后再用一次插入排序完成排序

如下图
第一趟排序,增量 h 取 a.length / 3+1 = 3,将原始数组按照增量划分为3个子数组,并对每一个子数组进行插入排序。
希尔排序(实现+总结)_第1张图片
每个子数组插入排序后,合并为一个数组,原始数组就变成了下图。

第二趟排序,增量h 取 h / 3+1 = 2,数组按照新的增量被划分为2个子数组,
对两个子数组进行插入排序。
希尔排序(实现+总结)_第2张图片
将子数组合并后,数组就变成了下图。
希尔排序(实现+总结)_第3张图片

第三趟排序,增量 h 取 h / 3+1 = 1,此时没有子数组了,直接对数组进行插入排序,得到结果。
希尔排序(实现+总结)_第4张图片
上个动图吧
希尔排序(实现+总结)_第5张图片
不过讲道理这个动图看起来还是蛮难理解的

2、代码实现

import java.util.Random;

public class ShellSort {

	public static void main(String[] args) {
		Random ra = new Random();
		int a [] = new int[15];
		for(int i = 0;i<15;i++) {
			a[i] = ra.nextInt(20)-5;        //随机15个数的数组
		}
		
		for(int num : a) System.out.print(num+" ");
		System.out.println("\n"+"排序后");
		ShellSort(a);
		for(int num : a) System.out.print(num+" ");

	}
	
	public static void ShellSort(int[] a) {
		int n = a.length;
		int h = 1;
		while(h < n/3) h = h*3+1;        //初始化增量,使h的值为小于a.length的第一个增量
		                                 //增量序列为: 1,4,13,40,121,364,···
		while(h >= 1) {                        //外层循环将h按照增量序列递减
			for(int i=h; i<n; i++) {
				int j;
				int temp = a[i];
				for(j=i; j>=h&&a[j-h]>temp; j-=h) {        //从待排序元素往前遍历比较,减量为h
					a[j] = a[j-h];
				}
				a[j] = temp;
			}
			h /= 3;
		}
	}

}

我们可以发现,将插入排序内循环中的减量改为h,然后再用一个使h按照增量序列递减的外循环包围就实现了希尔排序的代码

增量序列的选取
可能有人会好奇,到底怎么选择增量序列。
只要h1 = 1,任何增量序列都是可行的,最初shell提出取increment=n/2向下取整,increment=increment/2向下取整,直到increment=1。但由于直到最后一步,在奇数位置的元素才会与偶数位置的元素进行比较,这样使用这个序列的效率会很低。后来Knuth提出取increment=n/3向下取整+1。有很多论文研究了各种不同的序列,都无法证明某个序列是最好的。但应用不同的序列会使希尔排序算法的性能有很大的差异。

代码中的增量序列用的是:1,4,13,40,121,364,···

稳定性
希尔排序中相等数据可能会交换位置,所以希尔排序是不稳定的算法。

与插入排序比较
速度比插入排序快多了,而且数组越大,优势就越明显。
有意思的是,从插入排序到希尔排序只做了一点小小的改变,就突破了平方级别的运行时间

你可能感兴趣的:(算法)