Hoare版本(左右指针法)
霍尔(Sir Charles Antony Richard Hoare)是一位英国计算机科学家,也是快速排序算法的发明者。
算法思路:分治法
分治法就是大问题分解成各个小问题,对小问题求解,使得大问题得以解决。
快速排序是在每一轮挑选一个基准元素,并让比它大的元素移动到数列一边,比它小的元素移动到数列另一边,从而把数列拆解成两个部分。每一部分在下一轮又分别被拆分成两部分,直到不可再分为止。
单轮排序动图
代码实现
- 取基准元素
- 分区过程
- 对两个区间重复进行分区操作
function quickSort(arr, startIndex, endIndex) {
// 递归结束条件
if (startIndex >= endIndex) {
return;
}
// 每一轮排序后,都要定位pivot所在位置,然后再对其左右两侧的数组分别排序
let pivotIndex = partition(arr, startIndex, endIndex);
// 根据基准元素位置,分成两部分递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
function partition(arr, startIndex, endIndex) {
let i = startIndex;
let j = endIndex;
// 取第一个位置的元素作为基准元素
let pivot = arr[startIndex];
while (j != i ) {
//控制右侧指针比较并左移
while(arr[j] > pivot && i < j) {
j--;
}
//控制左侧指针比较并右移
while(arr[i] <= pivot && i < j) {
i++;
}
// 跳出上面两个循环后,说明左右指针都不动了.
// 这时如果左右指针还未重合,就要交换左右指针所指向的元素
if (i < j) {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
j--; //注意:交换所指向值之后,再次从右指针开始移动
}
}
// 左右指针重合,交换指向元素和基准元素pivot
arr[startIndex] = arr[j];
arr[j] = pivot;
return j;
}
let arr = [4,6,5,3,2,8,1,9,7];
quickSort(arr, 0, arr.length -1);
console.log(arr);
时间复杂度
对于基准元素的选择,最简单的方式选择数列的第一个元素。但是如果这个元素是该数列的最大值或最小值,则排序时无法将数列拆解成两部分,时间复杂度会退化成O(n^2)。
因此快速排序最坏时间复杂度是O(n^2),最好的时间复杂度是O(nlogn)。
平均时间复杂度是O(nlogn)。
空间复杂度
在对序列的操作过程中只需花费常数级的空间,因为没有用到额外开辟的集合空间,空间复杂度是O(1)。
但需要注意执行递归操作所需要的内存空间和递归的深度成正比,因此最坏的空间复杂度是O(n), 最好的空间复杂度是O(logn)。
算法稳定性
快速排序是不稳定的排序算法。因为我们无法保证相等的数据按顺序被扫描到和按顺序存放。