深入理解希尔排序

希尔排序是一种基于插入排序的算法,相较于插入排序一点一点的移动元素,希尔排序实现了快速移动一大步。

对于大规模乱序数组插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组的一端移动到另一端。假如,如果主键最小的元素正好在数组的尽头,要将它挪到正确的位置就需要N-1次移动。希尔排序为了加快速度简单的改进了插入排序,交换不相邻的元素对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。

希尔排序的思想是使数组中任意间隔为h的元素都是有序的。这样的数组被称为h有序数组。换句话说,一个h有序的数组就是h个互相独立的有序数组编织在一起组成的一个数组。

《深入理解希尔排序》

在进行排序的时候,如果h很大,我们就能将元素移动到很远的地方,为实现跟小的h有序创造方便。用这种方式,对于任意以1结尾的h序列,我们都能够将数组排序。这就是希尔排序。

以下算法实现使用了序列1/2(3^k-1),从N/3开始递减至1,我们可以称这个序列称为递增序列。

public static void sort(Comparable[] a) {
	//将a[]按升序排列 	int N = a.length;
	int h = 1;
	while(h < N/3)	h = 3*h + 1;
	while(h >=1 ) {
		//将数组变为h有序 		for(int i = h;i < N;i++) {
			//将a[i]插入到a[i-h],a[i-2*h],a[i-3h]...之中。 			for(int j = i;j >= h && less(a[j], a[j-h]); j -= h)
				exch(a, j, j-h);
		}
		h = h/3;  //使间隔逐步变小 	}
}

实现希尔排序的另一种方法是对于每个h,用插入排序将h个子数组独立地排序。但因为子数组使相互独立的,一个更简单的方法是在h-子数组中将每个元素交换到比它大的元素之前去(将比它大的元素向右移动一格)。只需要在插入排序的代码中将移动元素的距离由1改为h即可。这样,希尔排序的实现就转化为了一个类似于插入排序但使用不同增量的过程。

希尔排序更高效的原因使它权衡了子数组的规模有序性。在排序之初,各个子元素都很短,排序之后子数组都是部分有序的,这两种情况都很适合插入排序。子数组有序的程度取决于递增序列的选择。

如何选择递增序列呢?

要回答这个问题并不简单。算法的性能不仅取决于h,还取决于之间的数学性质,比如它们的公因子等。首先递增序列应该是互质的。

和选择排序以及插入排序形成对比的是,希尔排序也可以用于大型数组。它对任意排序(不一定是随机的)的数组表现的也很好。

《深入理解希尔排序》
希尔排序详细轨迹
《深入理解希尔排序》
《深入理解希尔排序》 希尔排序可视轨迹

通过提升速度来解决其他方式无法解决的问题是研究算法设计和性能的主要原因之一

有经验的程序员又是会选择希尔排序,因为对于中等大小的数组它的运行时间是可以接受的。它的代码量很小,且不会使用额外的内存空间。

    原文作者:SeYuFac
    原文地址: https://zhuanlan.zhihu.com/p/61488756
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞