1. 程式人生 > >2、golang之快速排序

2、golang之快速排序

1、快速排序穩定性

快速排序是不穩定的演算法,它不滿足穩定演算法的定義。 演算法穩定性 -- 假設在數列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;並且排序之後,a[i]仍然在a[j]前面。則這個排序演算法是穩定的!  

2、快速排序時間複雜度

快速排序的時間複雜度在 最壞情況下是O(N²)平均的時間複雜度是O(N*lgN)。 這句話很好理解:假設被排序的數列中有N個數。遍歷一次的時間複雜度是O(N),需要遍歷多少次呢? 至少lg(N+1)次,最多N次。 (01) 為什麼最少是lg(N+1)次?快速排序是採用的分治法進行遍歷的,我們將它看作一棵二叉樹,它需要遍歷的次數就是二叉樹的深度,而根據完全二叉樹的定義,它的深度至少是lg(N+1)。因此,快速排序的遍歷次數最少是lg(N+1)次。 (02) 為什麼最多是N次?這個應該非常簡單,還是將快速排序看作一棵二叉樹,它的深度最大是N。因此,快讀排序的遍歷次數最多是N次。  

3、示例

// golang
func quickSort(arr []int, start, end int) {
    if start < end {
        i, j := start, end
        key := arr[(start+end)/2]
        for i <= j {
            for arr[i] < key {
                i++
            }
            for arr[j] > key {
                j--
            }
            if i <= j {
                arr[i], arr[j] = arr[j], arr[i]
                i++
                j--
            }
        }

        if start < j {
            quickSort(arr, start, j)
        }
        if end > i {
            quickSort(arr, i, end)
        }
    }
}

4、演算法流程分析

快速排序(Quick Sort)使用分治法策略。 它的基本思想是: 選擇一個基準數,通過一趟排序將要排序的資料 分割成獨立的兩部分;其中 一部分的所有資料都比另外一部分的所有資料都要小。然後,再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。 快速排序流程:
  1. 從數列中挑出一個基準值。
  2. 將所有比基準值小的擺放在基準前面,所有比基準值大的擺在基準的後面(相同的數可以到任一邊);在這個分割槽退出之後,該基準就處於數列的中間位置。
  3. 遞迴地把"基準值前面的子數列"和"基準值後面的子數列"進行排序。
  下面以數列a={30,40,60,10,20,50}為例,演示它的快速排序過程(如下圖)。 上圖只是給出了第1趟快速排序的流程。在第1趟中,設定x=a[i],即x=30。 (01) 從"右 --> 左"查詢小於x的數:找到滿足條件的數a[j]=20,此時j=4;然後將a[j]賦值a[i],此時i=0;接著從左往右遍歷。 (02) 從"左 --> 右"查詢大於x的數:找到滿足條件的數a[i]=40,此時i=1;然後將a[i]賦值a[j],此時j=4;接著從右往左遍歷。 (03) 從"右 --> 左"查詢小於x的數:找到滿足條件的數a[j]=10,此時j=3;然後將a[j]賦值a[i],此時i=1;接著從左往右遍歷。 (04) 從"左 --> 右"查詢大於x的數:找到滿足條件的數a[i]=60,此時i=2;然後將a[i]賦值a[j],此時j=3;接著從右往左遍歷。 (05) 從"右 --> 左"查詢小於x的數:沒有找到滿足條件的數。當i>=j時,停止查詢;然後將x賦值給a[i]。此趟遍歷結束! 按照同樣的方法,對子數列進行遞迴遍歷。最後得到有序陣列!