前言:看了网上不少讲解的,感受都不是很直观。因此我打算用一个白话文把希尔算法讲清楚。了解的都知道,他实际上是插入排序的改进版本。咱们都知道若是一组数据中,其局部有序数字越多,插入排序越高效。(若是有不了解的,能够去了解)。知道这个特色以后,希尔算法由此而生。java
//好比有组数据:
int[] arr = {8, 6, 2, 3, 1, 5, 7, 4};
复制代码
文字概述: 假设第一个元素为最小数字。那么咱们就要从index=1开始日后循环,依次拿后面的数字,与前面的数字依次比较,找出最小数字。也就是当index =1,那就是 arr[1] 和 arr[0] 比较。当index = 2 时,就是用 arr[2] 与 arr[1] 比较得出一个最小值,而后再与 arr[0] 比较。最终完成从小到大的排序。算法
因此代码以下数组
//{8, 6, 2, 3, 1, 5, 7, 4}
public void inseartSort(int[] arr) {
//因此外层循环,咱们跳过第一个元素 8,拿后面的数和前面的数比较
for (int i = 1; i < arr.length; i++) {
//一、这层的循环是拿后面的数和前面的【依次比较】得出最小数
//二、下面内层循环不知道怎么写,咱们能够先来拆分下逻辑
// (1)先看外层循环,index = 1,咱们要拿arr[1]与arr[0]比较,较小的数字排在前面
// (2)继续看外层循环,index = 2,咱们首先要拿arr[2]和arr[1]做比较,得出最小数后,继续和arr[0]作比较。
// 至关于index--了。
// (3)综上所述:外层循环的起始点,就是内层循环的起始点,其次index--才能依次和前面的数比较,作好排序。
// 既然是index--,那么index要知足的条件是index > 0 。否则异常。因此代码以下
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
复制代码
了解过插入排序算法的都知道。一组很长的数据里。若是局部有序数字越多,插入排序越高效,性能越好。
希尔算法: 就是利用了这个插入排序的特色改进的。他这里有个增量gap的概念。咱们暂且不去探讨增量序列带来的时间复杂度。markdown
假如仍是那组数据:{8, 6, 2, 3, 1, 5, 7, 4}。假设这里的增量是 gap = arr.length/2。也就是4,他会对数组,逻辑分组成几份(这里也能当作,增量是几就是分几份),而后依次对逻辑分组里进行插入排序,其数组仍是同一个数组。排序好以后再用 gap /= 2。也就是2。继续逻辑分组,继续插入排序。这样原始数组内大部分数据都是有序数据。最后 gap = 1,对这大部分数据都是有序数据的数组进行插入排序。那么速度就快不少,性能方便也很高效。性能
可能这么说很笼统,请看剖析spa
gap = 4时,那么就是code
//数据就对应以下
8 6 2 3 1 5 7 4
| | | | | | | |
8--|--|--|--1 | | |
6--|--|-----5 | |
2--|--------7 |
3-----------4
复制代码
这样是逻辑分组,orm
8 6 2 3 1 5 7 4
| | | | | | | |
1--|--|--|--8 | | |
5--|--|-----6 | |
2--|--------7 |
3-----------4
复制代码
竖着看,此时排序获得: 1 5 2 3 8 6 7 4排序
gap为2时就相隔2去分组,固然也就是分红2组it
1 5 2 3 8 6 7 4
| | | | | | | |
1--|--2--|--8--|--7 |
| | | |
| | | |
5-----3-----6-----4
复制代码
gap=2时的分组为
那么就是
1 5 2 3 8 6 7 4
| | | | | | | |
1--|--2--|--7--|--8 |
| | | |
| | | |
3-----4-----5-----6
复制代码
竖着看,此时排序获得: 1 3 2 4 7 5 8 6
最后gap = 1的时候就是正常的插入排序了。可能有人说,按着增量作,最后一组数据并无大部分数字有序啊。由于为了讲解选了比较少的数据size=8的数据。若是是size>=100000的话会很是的明显。
经过上面的讲解,咱们知道。其实希尔排序多了个增量的概念,大致没有变化
首先咱们知道增量gap的变化
//不难理解,gap最终变化是4,2,1
for (int gap = arr.length / 2; gap > 0; gap /= 2) {}
复制代码
接下来咱们把gap=1时,也就是正常插入排序的代码直接搬进去
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0; j--) {
if (arr[j] < arr[j - 1]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
//上面式子看不大出来,咱们把它变一下以下
//看看哪里时候把gap加上
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j >= 1; j-=1) { //主要是修改了j>0 改为了 j>=1。 j-- 改为了 j-=1
if (arr[j] < arr[j - 1]) {
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
}
//改完以后,咱们把gap=1的状况改为gap。也就是把1改为gap变化以下
for (int gap = arr.length / 2; gap > 0; gap /= 2) {
for (int i = gap; i < arr.length; i++) {
for (int j = i; j >= gap; j -= gap) {
if (arr[j] < arr[j - gap]) {
int temp = arr[j - gap];
arr[j - gap] = arr[j];
arr[j] = temp;
}
}
}
}
复制代码
好了,希尔排序就是这样了!接下来咱们打印下,来验证最开始的逻辑: