原文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自己如今是基准点,再也不须要对其进行排序。