希尔排序是插入排序的一种,有关插入排序可以移步:直接插入排序。
希尔排序是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。
排序比较值相同时,前后位置不发生改变,则为稳定排序算法,反之为非稳定排序算法。
设计思想
希尔排序又称为缩小增量排序,基本思想是分组
的直接插入排序。由直接插入排序算法分析可知,若数据序列越接近有序,则时间效率越高;再者,当n(待排序长度)较小时,时间效率也高。希尔排序正是基于这两点对直接插入排序算法进行的优化:
- 将一个数据序列分为若干组,每组由若干相隔一段距离(称为
增量
)的元素组成,在一个组内采用直接插入排序算法进行排序。 - 增量初值通常为数据序列长度的一半,以后每趟增量减半,最后值为1。随着增量逐渐减小,组内元素个数增加,数据序列接近有序。
代码实现
序列:{38,55,65,97,27,76,27,13,19}
,序列长度为9,增量delta初值为4,序列分为4组进行直接插入排序,之后每趟增量以减半规律变化。经过3轮完成排序。
public static void shellSort(int[] keys) {
//①若干趟,控制增量每趟减半
for (int delta = keys.length / 2; delta > 0; delta /= 2) {
//②一趟分为若干组,每组直接插入排序
for (int i = delta; i < keys.length; i++) {
//keys[i]是当前待插入元素
int temp = keys[i], j;
//③组内直接插入排序,寻找插入位置
for (j = i - delta; j >= 0 && temp < keys[j]; j -= delta) {
//每组元素相距delta远
keys[j + delta] = keys[j];
}
keys[j + delta] = temp; //插入元素
}
System.out.print("delta = " + delta + "\t");
printKeys(keys);
}
}
运行结果
初始数组:
38 55 65 97 27 76 27 13 19
delta = 4 19 55 27 13 27 76 65 97 38
delta = 2 19 13 27 55 27 76 38 97 65
delta = 1 13 19 27 27 38 55 65 76 97
希尔排序算法共有三重循环:
- 最外层循环①for语句以增量delta变化控制进行若干趟扫描,delta初值为序列长度
n/2
,以后每趟减半,直至1. - 中间循环for语句进行每一趟扫描,序列分为delta组,每组由相距
delta
远的n/delta
个元素组成,每组元素分别进行直接插入排序。 - 最内层循环for语句进行一组直接插入排序,将一个元素keys[i] 插入到其所在组前面的排序子序列中。
算法分析
希尔排序算法增量的变化规律有多种方案。上述增量减半是一种可行方案。一旦确定增量的变化规律,则一个数据序列的排序趟数就确定了。初始当增量较大时,一个元素与较远的另一个元素进行比较,移动距离较远;当增量逐渐减小时,元素比较和移动距离较近,数据序列则接近有序。最后一次,再与相邻位置元素比较,决定排序的最终位置。
- 希尔排序算法的时间复杂度分析比较复杂,实际所需的时间取决于具体的增量序列,网上说法众多,最差情况为O(n^2)
- 希尔排序算法的空间复杂度为O(1)
- 因为分组进行比较,相同值可能会错过比较机会,所以希尔排序不稳定