摘要
看希尔排序需要先想象出一个二维的矩阵,在这个矩阵中,有多少列数据全看步长(一定的规则得到)。处理完之后,就再接着用另一个步长组成矩阵处理。直到步长全部使用完。
这里的巧妙之处就是没有把序列先处理成二维数组,而是通过与步长配合,依旧在一维的序列中处理。
逻辑
希尔排序相当于把序列当作一个矩阵,逐列进行排序。当全部排序完成,整个序列就完全有序
矩阵的列数取决于步长序列
流程
- 创建步长序列
- 从最大步长开始,整列排序,直到排序完成
实现
创建步长序列,这里是有一个数组存放步长数据。步长是序列的长度减半直到步长为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)
- 属于不稳定排序