如何分析排序演算法

程式設計師翔仔 發表於 2022-06-16
演算法

分析方法

執行效率

對於排序演算法執行效率的分析,不僅僅只是簡簡單單的一個時間複雜度。

還需要從以下方面進行分析:

  • 最好情況、最壞情況、平均情況時間複雜度。對於排序演算法來說,有序度不同的資料,對於排序的執行時間有一定的影響,從多個方面分析時間複雜度會更加準確
  • 時間複雜度的係數、常數、低階。在實際開發中,大多是對一些規模較小的資料進行排序,實際執行速度是非常快的,這時候也可以把係數、常數、低階考慮進來
  • 比較次數或交換(移動)次數。常見的排序演算法都是基於比較的,這時候會涉及到元素比較大小和元素交換或移動,這時候比較次數和交換次數也會影響到執行效率

記憶體消耗

在演算法中,記憶體消耗基本上通過空間複雜度來衡量。

但是,在排序演算法中,會有一個新的概念用來衡量記憶體消耗,即原地排序。原地排序演算法特指不需要另外空間儲存的排序演算法,空間複雜度能達到 \(O(1)\)

穩定性

針對排序演算法,還有穩定性這樣一個重要的度量指標。

這個概念是指,如果待排序的序列中存在值相等的元素,經過排序之後,相等元素之間原有的先後順序不變。

常見排序演算法

常見的排序演算法有很多,這裡列出 10 種排序演算法作比較。而這 10 種常見的排序演算法會根據是否進行比較分為兩種:

  • 比較類排序:氣泡排序、選擇排序、插入排序、希爾排序、堆排序、快速排序、歸併排序
  • 非比較排序:計數排序、桶排序、基數排序
排序演算法 平均時間複雜度 最壞時間複雜度 最好時間複雜度 空間複雜度 穩定性
氣泡排序 \(O(n^2)\) \(O(n^2)\) \(O(n)\) \(O(1)\) 穩定
選擇排序 \(O(n^2)\) \(O(n^2)\) \(O(n^2)\) \(O(1)\) 不穩定
插入排序 \(O(n^2)\) \(O(n^2)\) \(O(n)\) \(O(1)\) 穩定
希爾排序 \(O(n^{1.3 \sim 2})\) \(O(n^2)\) \(O(n)\) \(O(1)\) 不穩定
堆排序 \(O(n\log_2n)\) \(O(n\log_2n)\) \(O(n\log_2n)\) \(O(1)\) 不穩定
快速排序 \(O(n\log_2n)\) \(O(n^2)\) \(O(n\log_2n)\) \(O(n\log_2n)\) 不穩定
歸併排序 \(O(n\log_2n)\) \(O(n\log_2n)\) \(O(n\log_2n)\) \(O(n)\) 穩定
計數排序 \(O(n+k)\) \(O(n+k)\) \(O(n+k)\) \(O(n+k)\) 穩定
桶排序 \(O(n+k)\) \(O(n^2)\) \(O(n)\) \(O(n+k)\) 穩定
基數排序 \(O(n \times k)\) \(O(n \times k)\) \(O(n \times k)\) \(O(n+k)\) 穩定

如何選擇合適的排序演算法?

選擇依據

在實際開發的時候,並不是時間複雜度低的排序演算法就能適用於任何場景。

比如說,計數排序演算法適用於較集中的小範圍整數序列,桶排序演算法適用於容易劃分為桶的均勻整數序列,計數排序適用於可劃分為具有遞進關係的“位”的整數序列。

一般來說,對於小規模的資料進行排序時,可以選擇時間複雜度是 \(O(n^2)\) 的排序演算法;對於大規模的資料進行排序時,需要選擇時間複雜度是 \(O(n \log n)\) 的排序演算法;對於非比較類排序演算法,主要應用於特定的場景。

這樣選擇的原因是,時間複雜度為 \(O(n^2)\) 的排序演算法會比 \(O(n \log n)\) 的排序演算法的效率低,一般指的都是時間複雜度在沒有係數、常數、低階介入比較的情況,當真正使用的時候,這些是不可避免的。

因此,在實際使用時,有些時候 \(O(n^2)\) 的排序演算法也會比 \(O(n \log n)\) 的排序演算法的效率高。

通常,為了兼顧任意規模資料的排序,在一個方法中會使用到多種排序演算法。

排序實現 - Glibc

例如 Glibc 的 qsort() 函式,資料量較小時會優先使用歸併排序演算法來對輸入資料排序,當資料量比較大時,qsort() 會改用快速排序演算法來排序。

在歸併排序中,每個元素小於 32 時,會直接進行歸併排序;當有元素大於 32 時,則先將元素的指標拷貝到臨時空間,再使用歸併排序對指標進行排序。

在快排過程中,元素個數小於等於 4 個時候,會使用插入排序代替快速排序。

排序實現 - Java

例如 JDK 8 中 Arrays.sort() 的底層實現,也根據不同的情況使用到多種排序演算法。

對於元素個數小於 47 的序列,使用的是插入排序演算法;對於元素個數大於 47 而小於 286 的序列,使用的則是快速排序演算法。

而對於超過 286 個元素的序列,還會判斷這個序列是否結構化(資料是否時升時降),結構化的序列會使用歸併排序演算法,而非結構化的序列仍然會使用快速排序演算法。