原文javascript
Quicksort經過從數組中選取一個元素並將其表示爲基準點,把數組中的全部其餘元素分爲兩類 - 它們小於或大於此基準點。java
而後把做爲這一輪排序結果的兩個數組(數組元素都小於基準點的數組和數組元素都大於基準點的數組)再進行相同的排序。即分別再選個基準點,而後基於基準點分紅兩個數組元素分別小於和大於基準點的數組。算法
最終,因爲最後數組中沒有元素或只有一個元素,所以不用再比較了。剩下的值都已經基於基準點排好序了。數組
(譯者:內容有刪減,說的有些囉嗦)less
js的Array原型的sort方法使用另一種方法實現排序的,咱們不用這個實現快排。咱們本身建立一個方法,待排序的數組做爲參數,輸出排好序的數組。函數
const quickSort = (unsortedArray) => {
const sortedArray = TODO(unsortedArray);
return sortedArray;
};
複製代碼
因爲數組中項的「值」可能不是很明顯,咱們應該爲排序算法提供一個可選參數。在js中,字符串和數字會排好序的,可是對象不會。咱們要根據user對象的age字段給數組排序。ui
const defaultSortingAlgorithm = (a, b) => {
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
};
const quickSort = (
unsortedArray,
sortingAlgorithm = defaultSortingAlgorithm
) => {
const sortedArray = TODO(unsortedArray);
return sortedArray;
};
複製代碼
因爲咱們是不斷地重複找基準點,而後輸出全小於基準點和全大於基準點的數組的這個步驟。咱們但願用遞歸來實現,這樣能夠少寫代碼。this
你能夠隨便找個基準點:第一個、中間、最後一個、隨機一個。爲了簡單起見,咱們假設基準點的選取對時間複雜度沒有影響。我在本文中老是使用最後一個元素做爲基準點,由於要配合下圖的演示(圖中用的是最後一個元素,來源維基百科)。spa
數組基於基準點分紅兩個數組:小於基準點的數組放前面,大於基準點的數組放後面。最終,把基準點放在兩個數組的中間,重複以上步驟。code
爲了避免改變原數據,咱們建立了新數組。這不是必要的,可是是個好的習慣。
咱們建立recursiveSort做爲遞歸函數,它將遞歸子數組(從起始索引到結束索引),中途改變sortedArray數組的數據。整個數組是第一個傳遞給此遞歸函數的數組。
最後,返回排好序的數組。
recursiveSort函數有一個pivotValue變量來表示咱們的基準點,還有一個splitIndex變量來表示分隔小於和大於數組的索引。從概念上講,全部小於基準點的值都將小於splitIndex,而全部大於基準點的值都將大於splitIndex。splitIndex被初始化爲子數組的開頭,可是當咱們發現小於基準點時,咱們將相應地調整splitIndex。
咱們將循環遍歷全部值,將小於基準點的值移動到起始索引以前。
const quickSort = (
unsortedArray,
sortingAlgorithm = defaultSortingAlgorithm
) => {
// Create a sortable array to return.
const sortedArray = [ ...unsortedArray ];
// Recursively sort sub-arrays.
const recursiveSort = (start, end) => {
// If this sub-array contains less than 2 elements, it's sorted.
if (end - start < 1) { /*譯者:經熱心觀衆提醒,這裏應該是小於1,而不是小於2*/
return;
}
const pivotValue = sortedArray[end];
let splitIndex = start;
for (let i = start; i < end; i++) {
const sort = sortingAlgorithm(sortedArray[i], pivotValue);
// This value is less than the pivot value.
if (sort === -1) {
// If the element just to the right of the split index,
// isn't this element, swap them.
if (splitIndex !== i) {
const temp = sortedArray[splitIndex];
sortedArray[splitIndex] = sortedArray[i];
sortedArray[i] = temp;
}
// Move the split index to the right by one,
// denoting an increase in the less-than sub-array size.
splitIndex++;
}
// Leave values that are greater than or equal to
// the pivot value where they are.
}
// Move the pivot value to between the split.
sortedArray[end] = sortedArray[splitIndex];
sortedArray[splitIndex] = pivotValue;
// Recursively sort the less-than and greater-than arrays.
recursiveSort(start, splitIndex - 1);
recursiveSort(splitIndex + 1, end);
};
// Sort the entire array.
recursiveSort(0, unsortedArray.length - 1);
return sortedArray;
};
複製代碼
咱們將全部小於基準點的值移動到splitIndex指向的位置,其餘的值不動(默認狀況下,大於splitIndex,由於splitIndex從子數組的開頭開始)。
一旦子數組被排序好後,咱們將基準點放在中間,由於排序就是基於基準點排的,咱們知道它的位置。
左邊的全部值(從start到splitIndex - 1)都會被遞歸排序,而且右邊的全部值(從splitIndex + 1到end)也都會被遞歸排序。 splitIndex自己如今是基準點,再也不須要對其進行排序。