1. 希爾排序
上篇文章我們講述了插入排序,其效率高於冒泡以及選擇排序,但是插入排序有一個問題就是如果數組中較小的數字在後方,那麼數組需要移動的次數是很多的,於效率而言是不好的,由此我們引出了希爾排序。
我們可以將數組中較小的數先放在數組靠前的位置,之後再進行插入排序,這樣的話效率肯定會提升,此排序方法就爲希爾排序。
2. 實現思路
將數組分爲若干個小組,每個小組內進行插入排序。分小組的規則爲:定義一個變量,初始時定義其爲數組長度的一半,小組內每個元素相距小組長度個距離,每個小組進行插入排序,當最後只剩下一個小組後,進行插入排序以後就可以得到有序數組。
3. 圖解
–> 第一趟
對應箭頭的顏色即爲同一小組,數組長度爲10,所以第一次被分爲 10/2 個小組,也就是五個。在每個分組內進行插入排序,第一趟排序後:
–>第二趟
如圖,將數組分爲 5/2個小組,也就是2個小組,每個小組的每個元素之間相距爲2,繼續在每個小組內進行冒泡排序
4. 實現思路
- 首先我們發現,我們每次需要分小組,每次將小組數量分爲上一次的一半個,所以我們發現這可以使用一個循環進行:
for(int gap = arr.length/2; gap > 0; gap /= 2) - 之後我們只需要關注每一個小組內的冒泡排序了,這就是輕車熟路了,我們首先需要定義一個循環,來進行每個小組內的插入排序
for (int i = gap; i < arr.length; i++) - 進行了兩次循環後,我們現在已經分好了組,並且找到了插入排序的需要插入的數據arr[i],那麼我們現在就可以找到其之前的小組內成員,若小於前面的成員,則移位
int insertIndex = i;
int insertValue = arr[i];
while(insertIndex - gap >= 0 && insertValue < arr[insertIndex - gap]){
arr[insertIndex] = arr[insertIndex - gap];
insertIndex -= gap;
}
arr[insertIndex] = insertValue; - 經過以上步驟,等最後剩下一個小組以後,我們還會進行一次插入排序,相當於對數組進行插入排序,那麼數組就是有序的了,之後gap/2 = 0,不會進入分組的循環,流程結束
5. 代碼
java版本
private static void shellSort(int[] arr){
for (int gap = arr.length/2; gap > 0; gap /= 2){
for (int i = gap; i < arr.length; i++){
int insertIndex = i;
int insertValue = arr[i];
while(insertIndex - gap >= 0 && insertValue < arr[insertIndex - gap]){
arr[insertIndex] = arr[insertIndex - gap];
insertIndex -= gap;
}
arr[insertIndex] = insertValue;
}
}
}
初步測試(10個數據):
public static void main(String[] args) {
int[] arr = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
shellSort(arr);
System.out.println(Arrays.toString(arr));
}
輸出:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
速度測試(80000數據):
public static void main(String[] args) {
int[] arr = new int[80000];
Random random = new Random();
for (int i = 0; i < arr.length; i++){
arr[i] = random.nextInt(800000);
}
System.out.println(System.currentTimeMillis());
shellSort(arr);
System.out.println(System.currentTimeMillis());
System.out.println(Arrays.toString(arr));
}
輸出:
1585650645547
1585650645559
//下面打印的數組就不復制了,太長了,可以自己粘貼代碼測試,不過最好還是自己寫一遍
結論:
可以看到,希爾排序可太快咯,比起插入排序的效率已經高了三四層樓那麼高了
Go語言版本
//希爾排序
func shellSort(arr []int){
//將數組分小組
for gap := len(arr)/2; gap > 0; gap /= 2 {
//每個小組進行插入排序
for i := gap; i < len(arr); i++ {
insertIndex := i;
insertValue := arr[i];
for {
if (insertIndex - gap < 0 || insertValue >= arr[insertIndex - gap]){
break
}
arr[insertIndex] = arr[insertIndex - gap]
insertIndex -= gap
}
arr[insertIndex] = insertValue
}
}
}
速度測試(80000數據):
func main(){
arr := make([]int, 80000)
t := time.Now()
fomat := t.UnixNano()
rand.Seed(fomat)
for i := 0; i < len(arr); i++{
arr[i] = rand.Intn(80000)
}
t1 := time.Now()
shellSort(arr)
t2 := time.Since(t1)
fmt.Println(t2)
// arr := make([]int, 8)
// queen8(arr, 0)
// fmt.Println(count)
}
結果:
8.9435ms
複雜度分析
希爾排序複雜度比較複雜,這個就不討論了哈哈哈,反正不是O(n^3)
以夢爲馬,不負韶華