前言:看了網上不少講解的,感受都不是很直觀。因此我打算用一個白話文把希爾算法講清楚。瞭解的都知道,他實際上是插入排序的改進版本。咱們都知道若是一組數據中,其局部有序數字越多,插入排序越高效。(若是有不瞭解的,能夠去了解)。知道這個特色以後,希爾算法由此而生。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;
}
}
}
}
複製代碼
好了,希爾排序就是這樣了!接下來咱們打印下,來驗證最開始的邏輯: