Javascript演算法之快速排序
快速排序是一個時間複雜度為O(nlogn)的演算法,可以快速處理百萬數量級資料。
1.主要思想
第一步:找到一個基準值(v)
第二步:進行劃分,使得比基準值小的值在基準值(v)左邊,比基準值大的值在基準值(v)右邊
第三步:對劃分好的左右兩部分,再重複進行一、二步
2.舉個栗子
假設有一個數組[34,21,56,43,33,89,3]
第一步:選擇陣列的第一個值為"基準值"
第二步: 按照順序,將每個元素與"基準值"進行比較,形成兩個子集,一個"小於34",另一個"大於等於34"
第三步:對劃分好的左右兩部分,再重複進行一、二步
3.具體實現
在具體實現中會出現一個swap
方法,主要用於把陣列內的兩個位置進行交換,
function swap(arr, a, b) {
if (a===b) return
let c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
一、定義三個函式
quickSort函式: 主要用於被外部呼叫;引數為陣列;無返回值;
sort函式:主要用於遞迴處理陣列;引數為陣列和要進行劃分的陣列的起始下角標;無返回值;
_partation函式:用於處理劃分陣列;引數為陣列和要進行劃分的陣列的起始下角標;返回值為“基準值”所在位置
function quickSort (arr) {} function sort (arr, l, r) {} function _partation (arr, l, r) {}
二、quickSort函式
首先,檢查陣列元素的個數,小於等於1,返回;
function quickSort (arr) {
+ let n = arr.length
+ if (n<=1) return
}
其次,進行陣列的劃分
function quickSort (arr) {
let n = arr.length
if (n<=1) return
+ _partation(arr, 0, n-1)
}
三、sort函式
首先,確定遞迴的停止條件,當陣列的左角標大於等於右角標時,說明已經沒有再進行陣列劃分的必要了,直接返回
function sort (arr, l, r) {
+ if (l>=r) return
}
其次,進行陣列的劃分,得到“基準值”的位置
function sort (arr, l, r) {
if (l>=r) return
+ let j = _partation(arr, l, r)
}
最後,再次呼叫sort函式,繼續進行“基準值”左右兩個陣列劃分; [l…j-1]為小於“基準值”的元素集合
function sort (arr, l, r) {
if (l>=r) return
let j = _partation(arr, l, r)
+ sort(arr, l, j-1)
+ sort(arr, j+1, r)
}
四、_partation函式
首先,定義一些變數用來記錄關鍵資訊值
v: 代表“基準值”,這裡暫定陣列第一個元素為“基準值”,即l值;
lpos: 代表小於“基準值”部分的最後一個元素的下角標;
function _partation (arr, l, r) {
+ let lpos = l;
+ let v = arr[l];
}
然後,按照順序,與“基準值”進行比較,小於“基準值”的元素放到lpos
的下一個位置,並且lpos++
,讓lpos
繼續代表小於“基準值”部分的最後一個元素的下角標,直到迴圈結束
function _partation (arr, l, r) {
let lpos = l;
let v = arr[l];
+ for (let i = l+1;i<=r;i++){
+ if (arr[i]<v) {
+ swap(arr, i, lpos+1);
+ lpos++;
+ }
+ }
}
接著,迴圈完成。把lpos
位置上的元素和l上的元素(即基準值)進行交換,實現lpos
位置之前的元素均小於“基準值”,並且返回“基準值”的位置
function _partation (arr, l, r) {
let lpos = l;
let v = arr[l];
for (let i = l+1;i<=r;i++){
if (arr[i]<v) {
swap(arr, i, lpos+1);
lpos++;
}
}
+ swap(arr, l, lpos );
+ return lpos;
}
五、優化
1.取基準值的優化
在陣列近乎有序時候,基準值從第一個元素開始取,也就是會獲得一個最小值,在它劃分時會出現,下面的情況,
例如:
…
原本最好可以劃分為3層,但是這裡卻變成了7層,演算法會退化成O(n^2)級別。
優化方法:基準值變成一個隨機數,這樣在資料足夠大時,每次取到最小值的概率會變得非常小。
function _partation (arr, l, r) {
let lpos = l;
+ let randomIndex = parseInt(Math.random() * (r - l + 1) + l);
+ swap(arr, l, randomIndex);
let v = arr[l];
for (let i = l+1;i<=r;i++){
if (arr[i]<v) {
swap(arr, i, lpos+1);
lpos++;
}
}
swap(arr, l, lpos );
return lpos;
}
2.當要劃分的陣列,個數小到一定程度的時候,可以藉助插入排序,完成陣列排序。
插入排序也是個神奇的演算法,以後會總結,這裡就先不說了。
這是本人第一次總結所學知識,行文思路不是很好,本文內容有借鑑阮一峰老師在寫快速排序分析的思路,寫的不好,望大家海涵,內容有誤的地方,望大家指出。
參考資料:
https://github.com/liuyubobobo/Play-with-Algorithms
http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html