一、找出
Array.prototype.sort
使用的什麼排序算法javascript二、用一種直觀的方式展現
Array.prototype.sort
的時間複雜度,看看它有多快?html三、實際開發中要注意的問題前端
Array.prototype.sort
各瀏覽器的算法實現ECMAScript 5.1java
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 Chrome
對 sort
作了特殊處理,對於長度 <= 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 Edge
和 IE(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 排序算法彙總