排序思想
◼希尔排序把序列看作是一个矩阵,分成 𝑛 列,逐列进行排序
 从某个整数逐渐减为1
当 𝑛 为1时,整个序列将完全有序

◼ 矩阵的列数取决于步长序列(step sequence)
✓ 比如,如果步长序列为{1,5,19,41,109,…},就代表依次分成109列、41列、19列、5列、1列进行排序
✓ 不同的步长序列,执行效率也不同

◼ 希尔本人给出的步长序列是 𝑜/2 𝑘 ,比如 𝑜 为16时,步长序列是{1, 2, 4, 8}

◼ 不难看出来,从8列 变为 1列的过程中,逆序对的数量在逐渐减少
因此希尔排序底层一般使用插入排序对每一列进行排序,也有很多资料认为希尔排序是插入排序的改进版

如何计算每一列的每个元素的索引

◼ 假设元素在第 col 列、第 row 行,步长(总列数)是 step
那么这个元素在数组中的索引是 col + row * step
比如 9 在排序前是第 2 列、第 0 行,那么它排序前的索引是 2 + 0 * 5 = 2
比如 4 在排序前是第 2 列、第 1 行,那么它排序前的索引是 2 + 1 * 5 = 7

希尔排序——实现

内层for循环即为对每一列进行插入排序

Java实现希尔排序_数据结构


◼最好情况是步长序列只有1,且序列几乎有序,时间复杂度为 O(n)

◼由于其是原地排序,空间复杂度为O(1),属于不稳定排序。◼ 希尔本人给出的步长序列,最坏情况时间复杂度是 O(n^2 )

Java实现希尔排序_算法_02

◼ 目前已知的最好的步长序列,最坏情况时间复杂度是 O(n 4/3 ) ,1986年由Robert Sedgewick提出

Java实现希尔排序_排序算法_03


完整实现代码及注释

package com.mj.sort.cmp;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import com.mj.sort.Sort;

@SuppressWarnings("unused")
public class ShellSort<T extends Comparable<T>> extends Sort<T> {

@Override
protected void sort() {
List<Integer> stepSequence = sedgewickStepSequence();
for (Integer step : stepSequence) {
sort(step);
}
}

/**
* 分成step列进行排序
*/
private void sort(int step) {
// col : 第几列,column的简称
for (int col = 0; col < step; col++) { // 对第col列进行排序
// col、col+step、col+2*step、col+3*step
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;
}
}
}
}

private List<Integer> shellStepSequence() {
List<Integer> stepSequence = new ArrayList<>();
int step = array.length;
while ((step >>= 1) > 0) {
stepSequence.add(step);
}
return stepSequence;
}
//
private List<Integer> sedgewickStepSequence() {
List<Integer> stepSequence = new LinkedList<>();
int k = 0, step = 0;
while (true) {
if (k % 2 == 0) {
int pow = (int) Math.pow(2, k >> 1);
step = 1 + 9 * (pow * pow - pow);
} else {
int pow1 = (int) Math.pow(2, (k - 1) >> 1);
int pow2 = (int) Math.pow(2, (k + 1) >> 1);
step = 1 + 8 * pow1 * pow2 - 6 * pow2;
}
if (step >= array.length) break;
stepSequence.add(0, step);
k++;
}
return stepSequence;
}
}