《算法(第4版)》学习-希尔排序

为什么希尔排序效率高

插入排序的两个特点:

(1)元素越少排序越快;

(2)元素有序程度越高排序越快。

希尔排序充分利用了这两个特点使用插入排序算法对数据进行排序。

使用插入排序时需要选用一个递增序列,该递增数列的第一个元素必须是1。

递增序列的元素将作为希尔排序的增量。希尔排序增量对待排序的元素进行分组。

 

第一次循环,使用递增序列的倒数第一个值作为增量对元素进行分组,然后使用插入排序对每个分组进行排序;

第二次循环,再使用递增数列的倒数第二个值作为增量对元素进行分组;

直到递增序列的最后一个值。

递增序列的第一个值是1,也就是说,最后一次循环时,将使用1作为增量,也就是将全部元素化为一组,然后使用插入排序进行最后的排序。

在这个过程中,可以看到,在前面的循环中增量较大,因此每个分组中的元素较少。虽然此时数组的有序度比较差,但元素较少所以插入排序效率依然很高。

随着循环的次数增多,增量越来越小,分组中的元素越来越多,但是元素的有序度越来越高,所以插入排序的效率依然很高。

综上所述,希尔排序就是充分利用插入排序的特点,然后构造出高效插入排序算法的所需特殊条件,使得插入排序算法可以高效进行。

希尔排序轨迹分析

下面以《算法(第四版)》中的算法2.3为例,讲解希尔排序的基本原理。

public class Shell{
    public static void sort(Comparable[] a) {
		int N = a.length;
		int h = 1;
		while (h < N / 3) {
			h = 3 * h + 1; //1, 4, 13
		}
		while (h >= 1) {
			for (int i = h; i < N; i++) {
				for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
					exch(a, j, j - h); //交换元素
				}
			}
			h = h / 3;
		}
	}
}

在《算法(第四版)》中的算法2.3中,作者使用了递增序列[1, 4, 13, 40, 121, 364, 1093,...]。

待排序的元素序列为 [S H E L L S O R T E X A M P L E],一共16个元素。

当增量h=13时,分为3组(以元素索引代替元素):[0,13],[1,14],[2,15]。

希尔排序轨迹 h = 13
 比较的元素0123456789101112131415
  SHELLSORTEXAMPLE
i=13[0, 13]PHELLSORTEXAMSLE
i=14[1, 14]PHELLSORTEXAMSLE
i=15[2, 15]PHELLSORTEXAMSLE

红色代表两个元素交换了位置,蓝色代表元素只是进行了比较但没交换位置。

当增量h=4时,分为4组:[0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15]。

希尔排序轨迹 h = 4
 比较的元素0123456789101112131415
i=4[0, 4]LHELPSORTEXAMSLE
i=5[1, 5]LHELPSORTEXAMSLE
i=6[2, 6]LHELPSORTEXAMSLE
i=7[3, 7]LHELPSORTEXAMSLE
i=8[0, 4, 8]LHELPSORTEXAMSLE
i=9[1, 5, 9]LEELPHORTSXAMSLE
i=10[2, 6, 10]LEELPHORTSXAMSLE
i=11[3, 7, 11]LEEAPHOLTSXRMSLE
i=12[0, 4, 8, 12]LEEAMHOLPSXRTSLE
i=13[1, 5, 9, 13]LEEAMHOLPSXRTSLE
i=14[2, 6, 10, 14]LEEAMHLLPSORTSXE
i=15[3, 7, 11, 15]LEEAMHLEPSOLTSXR

 i = 4, i = 8, i = 12时,是对分组[0, 4, 8, 12]进行插入排序;

i = 5, i = 9, i = 13时,是对分组[1, 5, 9, 13]分组进行排序;

i = 6, i = 10, i = 14时,是对分组[2, 6, 10, 14]分组进行排序;

i = 7, i = 11, i = 15时,是对分组[3, 7, 11, 15]分组进行排序。

由此可以看出,对各个分组的排序是交替进行的,并不是分组进行的。

当h=1时,将全部元素分为1组:[0 - 15]。

希尔排序轨迹 h = 1
 比较的元素0123456789101112131415
i=1[0 - 1]ELEAMHLEPSOLTSXR
i=2[0 - 2]EELAMHLEPSOLTSXR
i=3[0 - 3]AEELMHLEPSOLTSXR
i=4[0 - 4]AEELMHLEPSOLTSXR
i=5[0 - 5]AEEHLMLEPSOLTSXR
i=6[0 - 6]AEEHLLMEPSOLTSXR
i=7[0 - 7]AEEEHLLMPSOLTSXR
i=8[0 - 8]AEEEHLLMPSOLTSXR
i=9[0 - 9]AEEEHLLMPSOLTSXR
i=10[0 - 10]AEEEHLLMOPSLTSXR
i=11[0 - 11]AEEEHLLLMOPSTSXR
i=12[0 - 12]AEEEHLLLMOPSTSXR
i=13[0 - 13]AEEEHLLLMOPSSTXR
i=14[0 - 14]AEEEHLLLMOPSSTXR
i=15[0 - 15]AEEEHLLLMOPRSSTX
  0123456789101112131415

当h=1时,就是普通的插入排序算法。虽然h=1时,元素较多,但是有序度已经较高,所以排序依然很快。

可以让其分组进行,而不是交替进行。可以这样修改:

public class Shell {
	public static void sort(Comparable[] a) {
		int N = a.length;
		int h = 1;
		while (h < N / 3) {
			h = 3 * h + 1;
		}
		while (h >= 1) {
			for(int k = 0; k < h && h +k < N; k++)
			{
				for (int i = h +k; i < N; i += h) {
					for (int j = i; j >= h && less(a[j], a[j - h]); j -= h) {
						exch(a, j, j - h);
					}
				}
			}
			
			h = h / 3;
		}
	}

上面代码的改动:

1. 将 i++ 修改为 i +=h,使得每次循环跳过h个元素,挑出一组元素集中进行插入排序;

2. 添加一个for循环控制 i 的取值。

 

参考资料:1. 《算法(第四版)》 Robert Sedgewick著

                  2. https://blog.csdn.net/qq_39207948/article/details/80006224

                  3. .https://blog.csdn.net/weixin_37818081/article/details/79202115

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值