首页 > 技术文章 > 数据结构与算法-排序(七)希尔排序(Shell Sort)

shsuper 2021-08-17 21:07 原文

摘要

看希尔排序需要先想象出一个二维的矩阵,在这个矩阵中,有多少列数据全看步长(一定的规则得到)。处理完之后,就再接着用另一个步长组成矩阵处理。直到步长全部使用完。

这里的巧妙之处就是没有把序列先处理成二维数组,而是通过与步长配合,依旧在一维的序列中处理。

逻辑

希尔排序相当于把序列当作一个矩阵,逐列进行排序。当全部排序完成,整个序列就完全有序

矩阵的列数取决于步长序列

流程

  1. 创建步长序列
  2. 从最大步长开始,整列排序,直到排序完成

实现

创建步长序列,这里是有一个数组存放步长数据。步长是序列的长度减半直到步长为0 结束。这里必定会有步长是 1 的数据,所以不用担心序列没有完全排序完。

List<Integer> shellStepSequence() {
	List<Integer> stepSequence = new ArrayList<>();
	int step = array.length;
	while ((step >>= 1) > 0) {
		stepSequence.add(step);
	}
	return stepSequence;
}

按照步长序列遍历,序列就会按照不同的步长去处理排序。然后通过不断地缩小步长来使得序列逐渐成为一维的序列。

	List<Integer> stepSequence = shellStepSequence();
	for (Integer step : stepSequence) {
		sort(step);
	}

把每一列进行排序。数组中的索引是 col+row*step。这个地方就是巧妙的地方了,通过 col+row*step 来处理每一列中的元素比较排序处理。用这种方式就相当于把一个一维数组给拆分成每一行最多有 step 的元素的多列序列,即二维数组。

而这里的巧妙就是,给我们营造了一个二维序列的空间,实际的比较和交换逻辑还是在一维数组上进行,不用额外创造空间,这样的逻辑,真的是牛。

/*
 * 分成 step 列进行排序
 */
void sort(int step) {
	// col: 第几列
	for (int col = 0; col < step; col++) {
		// 对第 col 列进行排序
		// 对 [0, array.length) 进行插入排序
		for (int begin = col + step; begin < array.length; begin+=step) {
			int cur = begin;
			while (cur > col && cmp(cur, cur - step) < 0) {
				swap(cur, cur - step);
				cur -= step;
			}
		}
	}
}

时间和空间复杂度

  • 最好、平均时间复杂度:O(1)
  • 最坏时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 属于不稳定排序

推荐阅读