1. 程式人生 > >[golang] 數據結構-快速排序

[golang] 數據結構-快速排序

場景 就是 過程 大小 != ron col 使用 time

快速排序是個非常經典、高效、常用的排序算法。很多語言標準庫裏的排序算法都有用到它。

原理
快排原理其實比較簡單,就是將原本很大的數組拆成小數組去解決問題。
要拆就得找個拆的位置。如果吧這個位置稱為支點,那麽快速排序問題就變成了不斷的去找到拆分的支點元素位置。
通常找支點就是以某個元素為標準,分別從最右側元素向左找到比指定元素小的位置,再從最左側開始向右找比指定元素大的位置。如果兩個位置不相同就交換兩個位置,在繼續分表從兩頭相向尋找。找到合適的位置就是我們需要的支點。支點兩邊的元素再各自重復上面的操作,直到分拆出來的子數組只剩一個元素。分拆結束,順序也就拍好了。
那麽問題來了,以哪個元素為標準去比較呢?比如可以選第一個元素。

復雜度
理想情況下找到的支點可以把數組拆分成左右長度相近的子數組,此時時間復雜度為O(n*logn)
而最差情況則是每次找到的支點元素都在某一次,導致另一側完全浪費,尋找支點的過程也浪費。這個時候用時會達到O(n^2)。
由於會打亂相同元素原有的順序,所以快排也是一個不穩定排序。所以常用在普通類型數據的排序中。

代碼實現

package main

import (
    "time"
    "fmt"
    "math/rand"
)

func main() {
    var length = 10
    var list []int

    // 以時間戳為種子生成隨機數,保證每次運行數據不重復
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    for i := 0; i < length; i++ {
        list = append(list, int(r.Intn(50)))
    }
    fmt.Println(list)

    quickSort(list, 0, length-1)

    fmt.Println(list)
}

func quickSort(list []int, start, end int) {
    // 只剩一個元素時就返回了
    if start >= end {
        return
    }

    // 標記最左側元素作為參考
    tmp := list[start]
    // 兩個遊標分別從兩端相向移動,尋找合適的"支點"
    left := start
    right := end
    for left != right {
        // 右邊的遊標向左移動,直到找到比參考的元素值小的
        for list[right] >= tmp && left < right {
            right--
        }
        // 左側遊標向右移動,直到找到比參考元素值大的
        for list[left] <= tmp && left < right {
            left++
        }

        // 如果找到的兩個遊標位置不統一,就遊標位置元素的值,並繼續下一輪尋找
        // 此時交換的左右位置的值,右側一定不大於左側。可能相等但也會交換位置,所以才叫不穩定的排序算法
        if left < right {
            list[left], list[right] = list[right], list[left]
            fmt.Println(list)
        }
    }

    // 這時的left位置已經是我們要找的支點了,交換位置
    list[start], list[left] = list[left], tmp

    // 按支點位置吧原數列分成兩段,再各自逐步縮小範圍排序
    quickSort(list, start, left-1)
    quickSort(list, left+1, end)
}

運行結果
技術分享圖片

雜談
遇到最差情況時,上面算法的性能就比較糟糕了,和普通的插入排序差不多。所以為了避免選了個糟糕的支點,可以選擇數組中間元素作為對比的標準,或是選3個元素,取中間大小的元素作為參考項。這時可以在一定程度上優化性能。不過最快情況的場景是在太少見,基本可以忽略掉。
還有個可優化的點,就是在數據量很少時,快排並不能體現速度優勢,反而在二十幾個元素以內的排序中比插入排序還慢。所以可以在遞歸循環裏加個判斷,如果一側的元素數量小於某個值(比如20)時直接使用插入排序。

[golang] 數據結構-快速排序