方法一:单边循环法
第一步:选定基准元素pivot(为了方便我就选第一个作为基准元素);
第二步:设置一个mark指针,代表小于基准元素的区域边界;
第三步:从基准元素的下一个位置开始遍历数组;
🌟第四步:如果遍历到大于pivot的元素,就继续往后遍历,如果遍历到小于pivot的元素?
第1⃣️步:mark加1,代表小于pivot的元素增加1;
第2⃣️步:把遍历到的那个元素换到mark这个位置来;
第五步:mark最后所在的位置就是边界位置,把pivot和mark交换过来,返回这个位置就行;
第六步:利用分治策略,对mark两边递归排序;
/**
* 单边循环法的快速排序算法
* @param {需要排序的数组} arr
* @param {开始位置} startIndex
* @param {结束位置} endIndex
* @returns void 原地排序 修改原数组
*/
function quickSort(arr, startIndex, endIndex) {
if (startIndex >= endIndex) {
// 递归结束条件
return;
}
// 得到基准元素位置
let pivotIndex = partition(arr, startIndex, endIndex);
// 根据基准元素对左右两边进行递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
function partition(arr, startIndex, endIndex) {
// 取第一个作为基准元素
let pivot = arr[startIndex]
// mark左边是小于基准元素的值,mark右边是大于基准元素的值
let mark = startIndex;
// 从基准元素下一个位置开始遍历
for (let i = startIndex + 1; i <= endIndex; i++) {
// 遍历到小于基准元素,mark加一,然后mark和这个小的元素交换位置
if (arr[i] < pivot) {
mark++;
//这里使用的是数组的解构赋值
[arr[i], arr[mark]] = [arr[mark], arr[i]]
}
// 遍历结束后,arr[startIndex + 1] ~ arr[mark]的值是小于pivot的,arr[mark+1]~arr[endIndex]是大于pivot的
// 所以pivot的位置就确定了,下一步直接换位置就行
}
//数组结构赋值
[arr[startIndex], arr[mark]] = [arr[mark], arr[startIndex]]
return mark;
}
export default quickSort;
方法二:双边循环法
第一步:选取基准元素pivot(还是为了方便选第一个元素就行);
第二步:定义left和right两个指针,分别指向两端;
🌟第三步:从right开始向左移动,拿每一个元素与pivot比较,如果〈=pivot(表示这个值小于pivot,应该在左边),指针停止。开始移动left,left开始向右移动,如果〉=pivot(表示这个值应该在右边),然后交换这两个值。然后一直循环下去,直到left=right(两个指针在中间某个位置相撞);
第四步:交换pivot和重合位置的值,此时左边全是小于pivot的,右边全是大于pivot的;
第五步:接下来递归排序pivot的左右两边;
/**
*
* @param {*} arr
* @param {*} startIndex
* @param {*} endIndex
* @returns void 原地排序 直接修改原数组
*/
function quickSort(arr, startIndex, endIndex) {
// 递归结束条件
if (startIndex >= endIndex) {
return;
}
// 得到基准元素的位置
let pivotIndex = partition(arr, startIndex, endIndex);
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
/**
*
* @param {待排序数组} arr
* @param {开始位置} startIndex
* @param {结束位置} endIndex
* @returns 基准元素的下标
*/
function partition(arr, startIndex, endIndex) {
// 取第一个元素作为基准元素
let pivot = arr[startIndex];
let left = startIndex;
let right = endIndex;
// 左指针=右指针时代表已经全都比较完,此时的位置就是pivot应该在的位置
while (left !== right) {
// 右指针每次向左移动一位,直到遇到小于基准元素的值才停下来
while (left < right && arr[right] > pivot) {
right--;
}
// 右指针停下来后,左指针一样的每次向右移动一位,遇到大于基准元素的值停下来
while (left < right && arr[left] < pivot) {
left++;
}
// 如果是因为left=right才停下来,表示已经比较完,此时不需要交换位置,直接退出即可
if (left < right) {
// 利用数组的解构赋值,交换左右指针的值
[arr[left], arr[right]] = [arr[right], arr[left]];
}
}
// left和right重合了,此时将pivot和重合点交换即可。
[pivot, arr[left]] = [arr[left], pivot];
// 此时重合点就是pivot的位置,我用的left,使用right是一样的
return left;
}
export default quickSort;
其实还有第三种方法,不使用递归,使用栈的方式,但我不会。。。