快速排序到底有多快?(含代碼分析、9大排序算法並行運行對比視頻)


快排有多快

說到快我只推崇葵花寶典,那叫一個快啊~~~node

皮一下哈哈,言歸正傳。快速排序算法如其名同樣,快!來看看快排和其餘幾大排序算法的並行運行對比視頻(中間那個就是快排),你就知道它到底有多快了,請全屏橫屏播放更清晰:linux

啥是快排?

分治思想

  • 從待排元素集中選取一個元素做爲擺動基準pivot,pivot這詞比較形象,如上圖像一個軸同樣在擺動。記爲P
  • 將元素從新排列爲3個子塊:
    1. 左子塊S1:由 P的元素組成
    2. 中間塊M:僅有P一個元素
    3. 右子塊S2:由≥P的元素組成
  • 對左子塊S1和右子塊S2遞歸地重複上述過程,Return {quicksort(S 1 ), P, quicksort(S 2 )}.

代碼實現

代碼以下:程序員

typedef int T_ELEMENT;
int partition(T_ELEMENT A[ ], int left, int right);
/* sort A[left..right] */
void quicksort(T_ELEMENT A[ ], int left, int right)
{  
  int q;
  if( right <= left )
      return;
  if ( right > left )
  {  
     q = partition(A, left, right);
     /* partition分塊後 */
     //-> A[left..q-1] ≤ A[q] ≤ A[q+1..right]
     quicksort(A, left, q-1);     
     quicksort(A, q+1, right);
   } 
}

int partition(T_ELEMENT A[], int left, int right);

    T_ELEMENT P = A[left];
    i = left;
    j = right + 1;
    /*無限循環,使用break退出*/
    for(;;) 
    { 
        while (A[++i] < P) if (i >= right) break;
        /* 此時 A[i] ≥ P */
        while (A[--j] > P) if (j <= left) break;
        /* 此時 A[j] ≤ P */
        if (i >= j ) break/*退出for循環*/
        else swap(A[i], A[j]); 
    }
    if (j == left) return j ;
    swap(A[left], A[j]);
    return j;
}

舉栗子分析:web

分紅三塊了,再遞歸子塊迭代,直到right<=left. 這裏放一個全過程慢鏡頭動圖,幫助理解:面試

算法分析

  • 這種快速排序的優勢是咱們能夠「就地」排序,即無需依賴於輸入大小的臨時緩衝區。沒有緩衝區內存開銷,僅有棧開銷。(注還有一種非遞歸的棧實現版本,本文就先不聊了)
  • partition步驟:時間複雜度爲θ(n)。
  • 快速排序涉及分區和2個遞歸調用。故:

T(n) = θ(n) + T(i) + T(n-i-1) = cn+ T(i) + T(n-i-1)算法

其中,i是分區後第一個子塊的大小,將T(0)=T(1)= 1做爲初始條件。sql

  • 具體運行時間對不一樣特性的待排數據,其結果差別比較大,來看一下最好、最壞以及平均狀況分析。

最差狀況

  • 當待排數據序列爲正序或者逆序時,pivot將是在大小爲n的待排塊時中的最小(或最大)元素時。則階段1迭代中生成一個空子塊、pivot,及一個大小(n-1)的子塊,則時間複雜度爲θ(n)
  • 遞歸方程:

若是這種狀況在每一個分區中都重複發生,那麼每一個遞歸調用處理一個比前一個列表小1的列表。所以須要在達到大小爲1的列表以前進行n - 1次嵌套調用。這意味着調用樹是n - 1個嵌套調用的線性鏈。第i次調用須要作O(n-i)複雜度來進行分區,則編程

最好狀況

  • 如每次分區時樞軸(pivot)都能取到中間值,即每次分區後,將產生兩個大小大體相等的子塊,而且樞軸(pivot)元素處於中間值位置,須要作n次比較運算。數組

  • 遞歸方程:微信


如前所說,如每次執行分區時,都能將列表分紅兩個幾乎相等的兩個子塊。這意味着每次遞歸調用都要處理一個只有一半大小的列表。所以,在到達大小爲1的列表以前,咱們只能進行 嵌套調用。這意味着調用樹的深度爲 ,可是在調用樹的同一級別上沒有兩個調用處理原始列表的相同部分;所以,每一個級別的調用總共只須要O(n)個時間(每一個調用都有一些固定的開銷,可是因爲每一個級別上只有O(n)個調用,因此這被包含在O(n)因子中)。結果是,該算法只使用c(n log n)的時間。故時間複雜度爲O(n log n)。

平均狀況

要對n個不一樣元素的數組進行排序,快速排序須要O(n log n)的預期時間,推導很枯燥就不羅嗦了。

其餘排序算法

圖片來自wikipedia:

注:快排不須要額外的緩衝區開銷,可是須要棧開銷,其空間複雜度爲O(log n).

這裏對上表其中幾個效率相對較高的作個簡要介紹,後面若有機會再深刻學習總結:

  • Introsort內省排序,在C++ STL中有應用。內省排序(英語:Introsort)是由David Musser在1997年設計的排序算法。這個排序算法首先從快速排序開始,當遞歸深度超過必定深度(深度爲排序元素數量的對數值)後轉爲堆排序。採用這個方法,內省排序既能在常規數據集上實現快速排序的高性能,又能在最壞狀況下仍保持O(n log n) 的時間複雜度。因爲這兩種算法都屬於比較排序算法,因此內省排序也是一個比較排序算法。

  • Timsort排序算法:是一種混合穩定排序算法,它是從合併排序和插入排序中派生而來的,旨在對多種實際數據表現良好。由Tim Peters在2002年實現,用於Python編程語言。該算法查找已排序(運行)的數據的子序列,並使用它們對其他部分進行更有效的排序。這是經過合併運行直到知足特定條件來完成的。自2.3版以來,Timsort一直是Python的標準排序算法。還應用在Android平臺上的Java SE 七、GNU Octave(是一個開源的類MATLAB數序軟件)、V8(開源Java script引擎)以及Swift中,用於對非原始類型的數組進行排序。

  • MergeSort歸併排序:在計算機科學中,是一種高效的,通用的,基於比較的排序算法。大多數實現產生穩定的排序,這意味着相等元素的順序在輸入和輸出中是相同的。歸併排序是約翰·馮·諾伊曼(John von Neumann)在1945年發明的分而治之算法。早在1948年,Goldstine和von Neumann的報告就對自下而上的合併排序進行了詳細描述和分析。


  • Tournament sort:經過使用優先級隊列來查找排序中的下一個元素,它改進了選擇排序。在原始的選擇排序中,須要O(n)個操做才能選擇n個元素中的下一個元素;在錦標賽排序中,須要進行O(log n)運算(在O(n)中創建初始錦標賽以後)。錦標賽排序是堆排序的一種變體。

  • 樹形選擇排序又稱錦標賽排序(Tournament Sort:是一種按照錦標賽的思想進行選擇排序的方法。首先對n個記錄的關鍵字進行兩兩比較,而後在 個較小者之間再進行兩兩比較,如此重複,直至選出最小的記錄爲止。

  • 塊排序或塊合併排序Block sort: 它將至少兩個合併操做與插入排序組合在一塊兒,以達到O(n log n)的位置穩定排序。合併兩個排序的列表,A和B,等價於將A分紅大小相等的塊,在特殊規則下將每一個塊插入到B中,併合並AB對。


  • 平滑排序smoothsort,是一種基於比較的排序算法。它是堆排序的一種變體,由Edsger Dijkstra於1981年發明併發布。它的時間複雜度上限是O(n log n),但它不是一個穩定的排序。平滑排序的優勢是,若是輸入已經排序到必定程度,那麼它會更接近O(n)的時間,而堆排序的平均值是O(n log n),而無論初始排序狀態如何。


  • 希爾排序Shellsort,也稱爲Shell排序或Shell的方法,是一種就地比較排序。它能夠被看做是交換排序(冒泡排序)或插入排序(插入排序)的泛化。該方法首先對彼此相距很遠的元素對進行排序,而後逐步縮小要比較的元素之間的差距。經過從相隔很遠的元素開始,它能夠比簡單的最近鄰交換更快地將一些位置錯誤的元素移動到正確的位置。Donald Shell在1959年出版了第一個版本。Shellsort的運行時間很大程度上依賴於它使用的間隙序列。

算法應用

說到排序算法複雜度,請必定要與應用場景結合。主要須要考慮待排數據的集的尺寸,若是數據量小的時候反而是插入排序算法應用最爲普遍;而對於海量數據場合,則應使用漸近有效排序策略。這是什麼意思呢?說白了就是常使用混合算法!主要策略是利用快速排序、堆排序或歸併排序將總體快速分治排序,同時對遞歸底部的小列表採用插入排序。事實上,在實際應用中有更復雜的變體,例如在Android,Java和Python中使用的Timsort(合併排序,插入排序和其餘邏輯),以及在某些C++中用的introsort(快速排序和堆排序) 在.NET中排序實現。

再說白一點,在海量數據場景,利用快速排序、堆排序或歸併排序將海量數據快速迭代成收斂的小塊,而在小塊中採用最爲常見的插入排序儘快完成小塊排序,小塊中採用插入排序則能夠更大程度減小遞歸深度。

總結一下

在信息時代,有海量信息須要處理,即使有很是強勁的處理器,但如沒有很好的算法,仍然沒法知足對這些信息的處理。在處理過程當中,免不了要進行信息進行排序,快排在時空兩個維度的開銷都比較均衡,大量的應用軟件、開發工具以及軟件包都基於快排作了大量的應用。因此說快速排序改變世界,我的認爲並不爲過。同時對於求職面試,快速排序算法也是高頻面試主題,值得深刻研究掌握。

 

  

從0學ARM專輯彙總

  0. 到底什麼是Cortex、ARMv八、arm架構、ARM指令集、soc?一文幫你梳理基礎概念【科普

  1. 從0開始學ARM-安裝Keil MDK uVision集成開發環境


  2. 從0開始學ARM-CPU原理,基於ARM的SOC講解




  3. 從0開始學ARM-ARM模式、寄存器、流水線



推薦閱讀


【1】Linux面試題100道,看看會多少?
【2】 Modbus協議概念最詳細介紹 必讀
【3】 I2C基礎知識入門
【4】如何用C語言操做sqlite3,一文搞懂
【5】 又一華爲程序員進了ICU:壓垮一個家庭,一張結算單就夠了! 必讀

 

進羣,請加一口君微信,帶你嵌入式入門進階。

 


點擊「閱讀原文」查看更多分享,歡迎點分享、收藏、點贊、在看




本文分享自微信公衆號 - 一口Linux(yikoulinux)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索