咱們知道,插入排序的原理是將一個數組當作兩段,一段有序的,一段無序的,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; } }
能夠發現,希爾排序跟插入排序十分類似
希爾排序的寫法思路:能夠先將普通的插入排序寫出來,而後在外層再套一層循環,而後替換插入排序中的全部