咱们知道,插入排序的原理是将一个数组当作两段,一段有序的,一段无序的,java
每次将无序的数字中第一个数在有序的一段中找到合适位置插入shell
插入排序有一个特征就是若是数组呈现接近有序,那么排序的速度就会很快数组
如:5, 1, 2, 7, 9, 8code
只需将1插到5前面,2插到一、5中间,7不动,9不动,8插入七、9中间blog
可是,若是出现较小的元素被放置在数组的末端,就会产生不少移动数组元素的开销排序
如:5,4,9,8,1,2索引
这时咱们就须要用到希尔排序,希尔排序也是一种插入排序,目的是将数组中的数字for循环
尽可能呈现有序的分布class
第一轮原理
第二轮
简单来讲就是,先从多分组、小范围起调整元素的大小分布,而后依次减小分组,增大范围,调整元素分布
每次将当前元素的数组索引以前全部属于同组的元素进行依次比较,将较大的元素日后放,较小的元素往前放
如:假如代码处在第二轮排序,数组从数字1开始遍历,此时前面只有3是同组元素,就和3比较,须要交换位置
此时组内元素分布为:1,3,0,9,7
遍历到0时,此时前有一、3是同组元素,则先比较0和3,须要交换0和3的位置,
此时组内元素分布为:1,0,3,9,7
而后比较1和0,须要交换1和0的位置,
此时组内元素分布为:0,1,3,9,7
这时,索引4及其前面的全部组内元素的分布顺序已经调整完毕,后面依次类推
交换法
类推
//希尔排序第一轮,将10个数组分红了5组 for (int i = 5; i < arr.length; i++) { //分红5组,那么每组内相邻数字的索引差为5 //由于采用交换法,相似冒泡,依次将相邻元素进行比较(相似一个窗口,窗口的两端是要比较的数,比较完后窗口向前移动) //窗口两端比较的数必定是在同一组的,且是相邻的,遍历第一组时,窗口大小为组内相邻数的索引差5 //第一组左端索引0,右端索引5,下一组左端1,右端6,依次向前移动窗口 for (int j = i - 5; j >= 0; j -= 5) { //窗口的滑动,就是在组内,尽可能将较大或者较小的元素后移,使组内元素尽可能呈现有序 //j就是左端的索引,i就是右端索引 //刚开始j=0,i=5 //第二层for循环就是调整数组中每一个元素在其所属组内所在位置以前的全部元素(并包括本身)进行调整有序 if (arr[j] > arr[j + 5]) { temp = arr[j]; arr[j] = arr[j + 5]; arr[j + 5] = temp; } } } > [3, 5, 1, 6, 0, 8, 9, 4, 7, 2]
//希尔排序第二轮,将10个数组分红了5/2=2组 for (int i = 2; i < arr.length; i++) { for (int j = i - 2; j >= 0; j -= 2) { //说明须要调整顺序,前面较大的数须要向后放 if (arr[j] > arr[j + 2]) { temp = arr[j]; arr[j] = arr[j + 2]; arr[j + 2] = temp; } } } > [0, 2, 1, 4, 3, 5, 7, 6, 9, 8]
//希尔排序第三轮,将10个数组分红了5/2/2=1组 for (int i = 1; i < arr.length; i++) { for (int j = i - 1; j >= 0; j -= 1) { //说明须要调整顺序,前面较大的数须要向后放 if (arr[j] > arr[j + 1]) { temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } > [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
最终实现
for (int gap = arr.length / 2; gap > 0; gap /= 2) {//控制分组数,分组数最小为1 //遍历各组中全部 for (int i = gap; i < arr.length; i++) {//i为各类分组下,第一个分组内的第二个元素的索引 for (int j = i - gap; j >= 0; j -= gap) {//遍历前面的组内元素 //说明须要调整顺序,前面较大的数须要向后放 if (arr[j] > arr[j + gap]) { temp = arr[j]; arr[j] = arr[j + gap]; arr[j + gap] = temp; } } } }
插入法
public static void shellSort2(int[] arr){ int insertVal = 0; int insertIndex = 0; for (int gap = arr.length / 2; gap > 0; gap /= 2) { //从gap个元素,逐个对其所在的组进行直接插入排序 for (int i = gap; i < arr.length; i++) { insertVal = arr[i]; insertIndex = i-gap; if(insertVal<arr[insertIndex]) { while (insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + gap] = arr[insertIndex]; insertIndex -= gap; } arr[insertIndex + gap] = insertVal; } } }
普通插入排序
for (int i = 1; i < arr.length; i++) { insertVal = arr[i]; insertIndex = i - 1; while (insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + 1] = arr[insertIndex]; insertIndex--; } //假如待插入值是12,理应插入位应在4的后面,循环会在4的索引停住,则须要插入到insertIndex+1 if(insertIndex!= (i-1)){ arr[insertIndex + 1] = insertVal; } }
能够发现,希尔排序跟插入排序十分类似
希尔排序的写法思路:能够先将普通的插入排序写出来,而后在外层再套一层循环,而后替换插入排序中的全部