快速算法原理
快速排序算法是被广泛认可的一种高效排序算法,被誉为十大经典算法之一。它需要在数组中找到一个数值,然后比该数值小的全都放到左边,比该数值大的全都放到右边。那么该数值就被放到了数组中正确的位置上。
左边的数组和右边的数组也是同样操作,找到一个值,左边放小的,右边放大的。
所以在形式上,有人叫它分而治之。但实际就是用递归,把某个值放入到数组正确的位置,然后重复操作。这个值在英文上是pivot。
一般翻译会叫做基准,也有人叫做枢纽。标准点就叫基准吧。
这里就有个很关键的问题,到底数组中该选哪个数作为基准。在理想情况下,好的基准正好将数组等分,那么每一次都是等分的话,达到最优的时间复杂度就是O(nlogn)
数组中第一个数,中间数或者最后一个数作为基准
将数组中的第一个数或者中间一个数或者最后一个数作为基准,它们的时间复杂度差不多,因为数组刚开始都是乱序的。
这类的快速排序比较好理解,代码也好写。
代码
function quickSort(array){
// 获取数组的长度
let length=array.length
// 之后要递归,所以给一个递归的终止条件
if(length<=0){
return array
}
// 开始的坐标,我们以数组第一个坐标为起点
let startIndex=0
// 结束的坐标,我们以数组最后一个坐标为终点
let endIndex=length-1
// 取两个坐标的中点坐标,如果是小数,则向下取整数
let pivotIndex=Math.floor((startIndex+endIndex)/2)
// 获取中点基准
let pivot=array[pivotIndex]
// 存放比基准小的值
let left=[]
// 存放比基准大的值
let right=[]
// 遍历整个数组
for (let i = 0; i < length; i++) {
// 如果遍历到的数正好等于基准值就不用存放了,跳过该次循环
if(i===pivotIndex){
continue
}
// 遍历到的数值
let res=array[i]
// 基准值> 遍历到的数值,就把该数值放入到left数组。否则(包含大于与等于)放入right数组
if(pivot>res){
left.push(res)
}else{
right.push(res)
}
}
// 最后将left数组与right数组递归。得到新的排好序的数组
return [...quickSort(left),pivot,...quickSort(right)]
}
console.log(quickSort([2,5,8,3,4,1]));
//[1, 2, 3, 4, 5, 8]
第一次结果:以8为基准,比它小的都在left数组了,大的都在right数组。因为8刚好是最大值,right就为空
第一次结束后就进入了quickSort(left),也就是从小的数组递归。它也会得到left与right数组,然后再从left数组递归。
第二次结果:以5为基准,left=[2,1],right=[5,4]
。。。
大致的效果像这样:
不要在意画功,领会精神就好。
如果面试需要写快速排序,在没有想到更好的方法前,不妨先把这种写上。它也是满足快速排序的分而治之的特点。
以随机数为基准
这种方式,顾名思义,它充满不确定性。有的时候能取到好的基准,有的时候不能。且随机数也会消耗一些性能。
三数取中
以上两种方法,可以看到它们是有可能取到一个空数组的。而三数取中它是在首尾中三个数先排好序,然后注意了,骚操作来了,把这排好序的中间那个数和数组的倒数第二个数交换位置,再以该数作为基准,这样就避免了取到空数组的尴尬。
举例:
[1,3,5,8,6,2]
首:1 尾:2 中:(0+6)/2=3(如果是小数就向下取整),数组中下标为3的是数值8
这三个数就组成的顺序是[1,2,8],所以以2为基准。
我们先让这三数在原数组中排好位置,数组就变为[1,3,5,2,6,8]。
然后记得是把中位数与倒数第二个数交换位置:[1,3,5,6,2,8]。
从i开始向右走,找寻比pivot要大的数值就停下,j向左走,找寻比pivot要小的数值就停下。 然后i和j的数值交换。到最后i>=j的时候。循环停止,把下标i对应的值与pivot交换。
把左边的值与i-1位置的值作为数组递归,i+1位置的值与右边的值做为数组递归。这就是差不多一个 完整的流程。与之前的排序相比,难度挺高不少。
代码
function getpivot(array, left, right) {
let center = Math.floor((left + right) / 2);
if (array[left] > array[center]) {
[array[left], array[center]] = [array[center], array[left]];
}
if (array[center] > array[right]) {
[array[center], array[right]] = [array[right], array[center]];
}
if (array[left] > array[center]) {
[array[left], array[center]] = [array[center], array[left]];
}
[array[center], array[right - 1]] = [array[right - 1], array[center]];
return array[right - 1];
}
function quickSort(array, left, right) {
if (left < right) {
const pivotValue = getpivot(array, left, right);
let i = left;
let j = right - 1;
while (true) {
while (array[++i] < pivotValue) {}
while (array[--j] > pivotValue) {}
if (i < j) {
[array[i], array[j]] = [array[j], array[i]];
} else {
break;
}
}
[array[i], array[right - 1]] = [array[right - 1], array[i]];
quickSort(array, left, i - 1);
quickSort(array, i + 1, right);
}
return array;
}
let arr1 = [10, 2, 3, 9, 8, 5, 6];
console.log(quickSort(arr1, 0, arr1.length - 1)); // [2, 3, 5, 6, 8, 9, 10]