function QuickSort(a, from, to) { // Insertion sort is faster for short arrays. if (to - from <= 22) { InsertionSort(a, from, to); return; } var pivot_index = $floor($random() * (to - from)) + from; var pivot = a[pivot_index]; // Pre-convert the element to a string for comparison if we know // it will happen on each compare anyway. var pivot_key = (custom_compare || %_IsSmi(pivot)) ? pivot : ToString(pivot); // Issue 95: Keep the pivot element out of the comparisons to avoid // infinite recursion if comparefn(pivot, pivot) != 0. a[pivot_index] = a[from]; a[from] = pivot; var low_end = from; // Upper bound of the elements lower than pivot. var high_start = to; // Lower bound of the elements greater than pivot. // From low_end to i are elements equal to pivot. // From i to high_start are elements that haven't been compared yet. for (var i = from + 1; i < high_start; ) { var element = a[i]; var order = Compare(element, pivot_key); if (order < 0) { a[i] = a[low_end]; a[low_end] = element; i++; low_end++; } else if (order > 0) { high_start--; a[i] = a[high_start]; a[high_start] = element; } else { // order == 0 i++; } } QuickSort(a, from, low_end); QuickSort(a, high_start, to); }
基本思想:數組
經過一趟循環將要排序的數據分割成獨立的兩部分。app
其中一部分的全部數據都比另一部分的全部數據都要小。dom
而後再按此方法對這兩部分數據分別進行快速排序。函數
整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列。性能
操做流程:ui
接着是取分界值,在傳入的起始索引和截至索引的差值中任取一個索引,該索引對應的值即爲分界值,保存副本spa
須要注意的是,key不該該在循環時被循環出來和自身比較code
建立兩個變量記錄已交換位置的索引。orm
一個初值等於截至索引,每交換一個值到右邊,則該變量減-1。最後該變量和截至索引即爲遞歸函數的from和to。blog
另外一個初值等於開始索引,每交換一個值到左邊,則該變量加1。最後開始索引和該變量即爲遞歸函數的from和to。
而後從起始索引加+1開始循環,這很好理解,第一個值是key,自己不須要分,並且只有一個值時沒有左邊右邊的概念。
比較結果大於0,放右邊。右邊的邏輯:(升降序是經過比較函數來控制)
比較結果小於0,放左邊。左邊的邏輯:
值相同時,保持原位便可。操做下一個值,令i++。
循環結束後,數組已被邊界值分紅了兩部分,每部分的起始、截至索引都已經記錄。
接着把這兩部分按照上述邏輯進行操做。循環往復。
以上都是在原數組中操做,並未引入新數組。
例子:
源碼在對未傳入比較函數的狀況下,對key 進行了toString處理,保證行爲的一致性。
源碼對比較函數進行了封裝。在未傳入比較函數時,對兩個參數進行了toString處理。
代碼:
function quickSort(a, from, to) { // 遞歸退出條件,當傳入的起始索引和截至索引差值小於2時,此時對應的數據只有一個。已經無需比較 if (to - from < 2) return; const key_index = Math.floor(Math.random() * (to - from)) + from; // key的索引,值的範圍在起始索引和截至索引中任取 const key = a[key_index]; // key 用於比較 // 須要注意的是,key不該該在循環時被循環出來和自身比較 // 這裏交換數組起始位置值和key值。 a[key_index] = a[from]; a[from] = key; let high_start = to; // 數組分邊後其中一邊的開始索引 let low_end = from; // 數組分邊後另外一邊的截至索引 // 循環從起始位置+1開始,判斷條件中high_start,每交換一個值到右邊,該值就減1 for (let i = from + 1; i < high_start;) { let el = a[i]; // 當前副本,須要被分邊 let result = el - key; // 比較。可封裝成比較函數,實現升降序 if (result > 0) { /** * 右邊。high_start--;把high_start對應的值賦給a[i]。 * 此時a[i]已經變了,須要從新比較。故不能i++。把el賦給a[high_start]。el完成分邊 */ high_start--; a[i] = a[high_start]; a[high_start] = el; } else if (result < 0) { /** * 左邊。 * 把左邊low_end對應的值賦給a[i]。而後el賦給a[low_end],el完成分邊。而後low_end++。 * 此時a[i]是key值。無需比較。繼續操做下一個值。即i++ * 當循環至邊界索引key_index時,該索引對應的是數組起始值。 * 由此數組每一個值均可以與key進行比較,而後被分邊。 */ a[i] = a[low_end]; a[low_end] = el; low_end++; i++; } else { i++; } } // 此時遞歸,傳入起始索引和截至索引,對兩部分數據的進行上述操做 quickSort(a, from, low_end) // 左邊組數據 quickSort(a, high_start, to) // 右邊組數據 }