深刻淺出 JavaScript 的 Array.prototype.sort 排序算法

本文要解決的問題

一、找出 Array.prototype.sort 使用的什麼排序算法javascript

二、用一種直觀的方式展現 Array.prototype.sort 的時間複雜度,看看它有多快?html

三、實際開發中要注意的問題前端

Array.prototype.sort 各瀏覽器的算法實現

ECMAScript 5.1java

ECMAScript 6.0git

ECMAScript 草案github

看完上面三個規範中 Array.prototype.sort 部分,咱們會發現 ECMAScript 不一樣版本規範對 Array.prototype.sort 的定義中沒有要求用什麼樣的排序方式實現 sort() 方法,也沒有要求是否要採用穩定排序算法(下文會解釋什麼是穩定排序算法)。web

所以各瀏覽器都給出本身的實現方式:算法

表格內容部分來自於維基百科chrome

瀏覽器 使用的 JavaScript 引擎 排序算法 源碼地址
Google Chrome V8 插入排序和快速排序 sort 源碼實現
Mozilla Firefox SpiderMonkey 歸併排序 sort 源碼實現
Safari Nitro(JavaScriptCore ) 歸併排序和桶排序 sort 源碼實現
Microsoft Edge 和 IE(9+) Chakra 快速排序 sort 源碼實現

源碼分析

V8 引擎的一段註釋數組

// In-place QuickSort algorithm.
// For short (length <= 10) arrays, insertion sort is used for efficiency.

Google Chromesort 作了特殊處理,對於長度 <= 10 的數組使用的是插入排序(穩定排序算法) ,>10 的數組使用的是快速排序。快速排序是不穩定的排序算法。

可是很明顯比咱們常見的快速排序要複雜得多,不過核心算法仍是快速排序。算法複雜的緣由在於 v8 出於性能考慮進行了不少優化。

再看 safari Nitro 引擎的一段代碼

if (typeof comparator == "function")
  comparatorSort(array, length, comparator);
else if (comparator === null || comparator === @undefined)
  stringSort(array, length);

  省略....

function stringSort(array, length)
{
  var valueCount = compact(array, length);

  var strings = @newArrayWithSize(valueCount);
  for (var i = 0; i < valueCount; ++i)
      strings[i] = { string: @toString(array[i]), value: array[i] };

  bucketSort(array, 0, strings, 0);
}

  省略....

function comparatorSort(array, length, comparator)
{
  var valueCount = compact(array, length);
  mergeSort(array, valueCount, comparator);
}

默認使用的桶排序,若是 sort 傳入的自定義函數做爲參數,就是用歸併排序(穩定排序算法)

Firefox 源碼就不貼了,上面的表格有源碼地址,使用的穩定排序算法 — 歸併算法。
Microsoft EdgeIE(9+) 使用的不穩定排序算法 - 快速排序。
可是 IE(六、七、8)使用的穩定算法。

各類算法的對比

排序類型 平均狀況 最好狀況 最壞狀況 輔助空間 穩定性
快速排序 O(nlogn) O(nlogn) O(n²) O(nlogn) 不穩定
歸併排序 O(nlogn) O(nlogn) O(nlogn) O(n) 穩定
插入排序 O(n²) O(n) O(n²) O(1) 穩定
桶排序 O(n+k) O(n+k) O(n²) O(n+k) (不)穩定

注: 桶排序的穩定性取決於桶內排序的穩定性, 所以其穩定性不肯定。

算法時間複雜度

在進行算法分析時,語句總的執行次數T(n)是關於問題規模n的函數,
進而分析T(n)隨着n的變化狀況並肯定T(n)的數量級。
算法的時間複雜度,也就是算法的時間度量,記做:T(n)=O(f(n))。
它表示隨問題規模n的增大,算法執行時間的增加率和f(n)的增加率相同,
稱做算法的時間複雜度,簡稱爲時間複雜度。
其中f(n)是問題規模n的某個函數。

經常使用的時間複雜度所耗費的時間從小到大依次是

O(1) < O(logn) < O(n) < O(nlogn) < O(n²) < O(n³) < O(2^n) < O(n!) < O(n^n)

圖片來源

算法的時間複雜度與運行時間有一些常見的比例關係 查看圖表來源

複雜度 10 20 50 100 1,000 10,000 100,000
O(1) < 1s < 1s < 1s < 1s < 1s < 1s < 1s
O(log(n)) < 1s < 1s < 1s < 1s < 1s < 1s < 1s
O(n) < 1s < 1s < 1s < 1s < 1s < 1s < 1s
O(n*log(n)) < 1s < 1s < 1s < 1s < 1s < 1s < 1s
O(n²) < 1s < 1s < 1s < 1s < 1s 2 s 3-4 min
O(n³) < 1s < 1s < 1s < 1s 20 s 5 hours 231 days
O(2^n) < 1s < 1s 260 days hangs hangs hangs hangs
O(n!) < 1s hangs hangs hangs hangs hangs hangs
O(n^n) 3-4 min hangs hangs hangs hangs hangs hangs

維基百科關於算法穩定性的解釋

當相等的元素是沒法分辨的,好比像是整數,穩定性並非一個問題。然而,假設如下的數對將要以他們的第一個數字來排序。

(4, 1)  (3, 1)  (3, 7)(5, 6)

在這個情況下,有可能產生兩種不一樣的結果,一個是讓相等鍵值的紀錄維持相對的次序,而另一個則沒有:

(3, 1)  (3, 7)  (4, 1)  (5, 6)  (維持次序)
(3, 7)  (3, 1)  (4, 1)  (5, 6) (次序被改變)

想看本身瀏覽器排序算法的穩定性? 點我

各類排序算法實現有多快?

咱們先經過這個在線網站大致測試一下

對一個有 10000 個元素的數組,快速排序 > 歸併排序 >>> 插入排序
並且插入排序大於 1s 了。

對於一個只有 10 個元素的數組,插入排序 > 快速排序
這也說明了爲何 chrome 在小於等於 10 個元素的小數組使用插入排序的緣由了。

瀏覽器的實現不一樣有什麼影響

排序算法不穩定有什麼影響

舉個例子:

某市的機動車牌照拍賣系統,最終中標的規則爲:

一、按價格進行倒排序;

二、相同價格則按照競標順位(即價格提交時間)進行正排序。

排序如果在前端進行,那麼採用快速排序的瀏覽器中顯示的中標者極可能是不符合預期的。

解決辦法

Array.prototype.sort 在不一樣瀏覽器中的差別和解決辦法

大致的思路就是,本身寫一個穩定的排序函數,以保持各瀏覽器的一致性。

工具

一、在線排序算法對比網站
二、排序算法視覺圖

擴展閱讀

一、快速排序(Quicksort)的Javascript實現
二、JS中可能用獲得的所有的排序算法
三、7 種經常使用的排序算法-可視化
四、深刻了解javascript的sort方法
五、JavaScript 排序算法彙總

參考文檔

聊聊前端排序的那些事
排序算法
JavaScript排序算法彙總

相關文章
相關標籤/搜索