希尔排序基本介绍
希尔排序是希尔(Donald Shell) 于 1959 年提出的一种排序算法。 希尔排序也是一种插入排序, 它是简单插入排序经过改进之后的一个更高效的版本, 也称为缩小增量排序。
希尔排序基本思想
希尔排序按照增量将数组进行分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止.
希尔排序图解(交换法)
第一次:gap = arr.length/5 = 5 , 将数组分为五组,每个数组元素的索引相差 5
如何完成第一次的排序?
仔细想想,我们需要用一次循环将每组中的元素排序
总共有五组,我们又需要一次循环
所以完成每次排序,需要两层循环
程序代码如下,把 i ,j 都看作是辅助指针:
i 与 j 配合使用,可以将指针从数组第一个元素,移动至最后一个元素,目的:把数组遍历一遍
j 与 i 配合使用,每次都从数组索引 i 处往前遍历,每次向前移动 gap 个位置,然后进行交换(冒泡排序的意思):看看前面的元素有没有比我的值大,如果前面的元素比我的值大,我就要和他交换位置,跑到前面去.
第二次:gap = gap /2 = 2; , 将数组分为两组,每个数组元素的索引相差 2
第一组:
i = 2 时,数组从索引 2 处往前遍历,间隔为 2 :将 arr[0]、arr[2] 排序
i = 4 时,数组从索引 4 处往前遍历,间隔为 2 :将 arr[0]、arr[2]、arr[4] 排序
i = 6 时,数组从索引 6 处往前遍历,间隔为 2 :将 arr[0]、arr[2]、arr[4]、arr[6] 排序
i = 8 时,数组从索引 8 处往前遍历,间隔为 2 :将 arr[0]、arr[2]、arr[4]、arr[6]、arr[8] 排序
第二组:
i = 3 时,数组从索引 3 处往前遍历,间隔为 2 :将 arr[1]、arr[3] 排序
i = 5 时,数组从索引 5 处往前遍历,间隔为 2 :将 arr[1]、arr[3]、arr[5] 排序
i = 7 时,数组从索引 7 处往前遍历,间隔为 2 :将 arr[1]、arr[3]、arr[5]、arr[7] 排序
i = 9 时,数组从索引 9 处往前遍历,间隔为 2 :将 arr[1]、arr[3]、arr[5]、arr[7]、arr[9] 排序
第三次:gap = gap /2 = 1; , 将数组分为一组,每个数组元素的索引相差 1 ,对于交换法而言,这就是异常冒泡排序
i = 1 时,数组从索引 1 处往前遍历,间隔为 1 :将 arr[0]、arr[1] 排序
i = 2 时,数组从索引 2 处往前遍历,间隔为 1 :将 arr[0]、arr[1]、arr[2] 排序
i = 3 时,数组从索引 3 处往前遍历,间隔为 1 :将 arr[0]、arr[1]、arr[2]、arr[3] 排序
总结:每次使用循环改变 gap 的值(初始值:数组大小/2 ,之后:gap = gap/2),然后在改变 gap 的循环中嵌套上面的双层 for 循环.
改变 gap :for (int gap = arr.length / 2; gap > 0; gap /= 2) {
内层循环:实现对每组数组的排序.
代码如下:
public static void shellSort(int[] arr) {
// 增量gap, 并逐步的缩小增量
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
// 从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
if (arr[j] < arr[j - gap]) {
while (j - gap >= 0 && temp < arr[j - gap]) {
// 移动
arr[j] = arr[j - gap];
j -= gap;
}
// temp 比 arr[j - gap] 大,所以需要插入在 j 的位置
arr[j] = temp;
}
}
}
}