面試官,您要的快排

今天看到 V2EX 上有人討論 社招還會問 「請手寫選擇排序算法」 嗎?,看來仍是有不少人關心的。結合本身最近面試的經歷,我能夠明確的告訴你們,相似這種問題,只要你的工做經驗小於 10 年,基本上逃不掉。勸你們不如抽點時間早作準備。面試

簡式快排

面試中遇到問快排的,如上面那個帖子中的狀況。你就能夠上一份簡式快排了,何謂簡式?最短的代碼表述快排的思想。算法

快排的思想,實質是分治法。基於什麼來分?找一個支點來分,一般稱之爲 pivot, 而這個分的過程稱之爲 partition, 基於以上兩點,咱們用遞歸的方式描述快排:ui

void quicksort(int arr[], int l, int r) {
    if (l < r) {
        int pivot = partition(arr, l, r);
        quicksort(arr, l, pivot-1);
        quicksort(arr, pivot+1, r);
    }
}

如何?簡單吧。有人說面試的時候手寫快排,若是提早沒有背下來的話,確定歇菜。我不認爲這樣基礎的算法是須要背的,上面這個遞歸,如此簡潔,如此美,真的須要硬記?code

有人說,這個好理解,關鍵在於 partition 如何實現。的確,partition 是快排的靈魂。CLRS 裏採用了以尾巴爲支點的策略,我在這裏與其保持一致:排序

int partition(int arr[], int l, int r) {
    int k = l, pivot = arr[r];
    for (int i = l; i < r; ++i)
        if (arr[i] <= pivot) std::swap(arr[i], arr[k++]);
    std::swap(arr[k], arr[r]);
    return k;
}

這算法用白話說,就是從頭至尾迭代,和支點比較,大的無論,小的換。換了才日後看。最後支點戳中間,算是分界線。遞歸

get

3,7,8,5,2,1,9,5,4

這麼來一下,就成了:it

3,2,1,4,7,8,9,5,5
^^^^^ | ^^^^^^^^^

而後一樣的手法分別解決兩邊,這樣遞歸的解下去。io

來份三明治

其實上面的那份,已經能夠解決面試中的問題了。但其實有很大的缺陷,如當全部元素都相同的狀況下,partition 將一直返回 r, 遞歸的深度高達 N,每一次遞歸中 partition 又循環 N 次,時間複雜度直接飆到了 O(N^2). 這顯然很是的不值當。class

因而咱們以爲分兩份太粗,分三份試試?

5 7 4 3 1 2 6 5 5

也來那麼一下,成爲:

4 3 1 2 5 5 5 7 6
^^^^^^^ |   | ^^^

這就是三明治的原理了,左邊是小於 pivot 的,中間是等於 pivot 的,右邊是大於 pivot 的。中間部分不參與遞歸,分治的是兩邊。

咱們需稍稍改變下 partition 的實現,顯然咱們此次但願返回的兩個支點(左右邊界):

// 3-way partition
std::pair<int, int> partition(int arr[], int l, int r) {
    int k = l, p = r;
    for (int i = l; i < p; )
        if (arr[i] < arr[r]) std::swap(arr[i++], arr[k++]);
        else if (arr[i] == arr[r]) std::swap(arr[i], arr[--p]);
        else ++i;
    // move pivots to centre
    int m = std::min(p-k, r-p+1);
    for (int i = 0; i < m; ++i)
        std::swap(arr[k+i], arr[r-i]);
    return std::make_pair(k, r-p+k);
}

quicksort 也須要稍稍改變一點:

void quicksort(int arr[], int l, int r) {
    if (l < r) {
        auto pivot = partition(arr, l, r);
        quicksort(arr, l, pivot.first-1);
        quicksort(arr, pivot.second+1, r);
    }
}

若是在遇到所有元素相同的狀況,時間複雜度成功的減小到 O(N). 算是一個突破吧。

東北亂燉

其實 CLRS 隨後還提到了以隨機位置爲 pivot 的思路,我稱之爲東北亂燉。那是隻針對 pivot 選取的改變,基於上述代碼,改造起來是很是容易的。這裏就不作過多實現,留做感興趣者本身練習吧。


上述三道菜,基本可以解決面試中可能遇到的種種狀況了。讓面試官吃飽,頗有必要~

相關文章
相關標籤/搜索