前言
如果想深入了解的话建议先看看 算法概述
为了方便用来测试的数组一成不变,我们可以来一个随机数组
const arr = []
const arrLength = 11 //可以随机长度 Math.floor(Math.random() * 30) + 1
for (let i = 0; i < arrLength; i++) {
arr.push(Math.floor(Math.random() * 99) + 1)
}
console.log(arr, 'arr')
算法描述
来到快排了,面试中最常见面的排序之一,这个排序比前面的要复杂些,先简单说一次思路
快排主要的思想是分治法,从数组中取出一个元素的值(默认我们取第一个元素)当基准(分界值),把数组中比基准值小的放左边,比基准值大的放右边,这样形成左边一个数组,右边一个数组
接着,我们按照上面的方式继续分治左右的这两个数组,直到左右两边的元素没法再分
下面我们来看图,看看如何比较基准来分成两组的
动图演示
定第一项的值39为基准,并空出位置(为了更形象)
先从右边开始比较大小,不小于39的略过,比较从右往左的下一个,小于39的时候,则把值赋予空位,并记录下此时的位置为right
接着从左边开始比较,不大于39的略过,比较从左往右的下一个,大于39的时候,则把值赋予空位,并记录下此时的位置为left
再接着从右边开始上面的操作,直到left和right相遇(即left不小于right了),到此,把数组分成了以基准(39)为分界线的两个数组了
然后我们再进行新一轮的基准定义,重复以上操作,直到分无可分
从上图可以看出,这个个过程是一个树状图的结构,一旦涉及树状图,那我们的函数思想就是递归了(不了解递归的自行百度)
代码实现
//快速
function quickSort(arr) {
//定义一个递归函数
const recursion = (arr, left = 0, right = arr.length - 1) => {
if (left < right) { //过程5:此时left===right,分无可分,则代表整个递归循环着结束
let i = left, j = right, pivot = arr[i];
while (i < j) { //过程4:i与j相遇则结束此次递归循环,此时i===j
//i++和j--是参与完逻辑运算后才自增,不太熟悉的同学可以去看看i++和++i的区别
while (i < j && arr[j] > pivot) j--
if (i < j) arr[i++] = arr[j] //这里用i++,被换过来的必然比x小,赋值后直接让i自加,不用再比较,可以提高效率,下同
while (i < j && arr[i] < pivot) i++
if (i < j) arr[j--] = arr[i]
}
arr[i] = pivot
recursion(arr, left, i - 1) //继续左边数组的分治
recursion(arr, i + 1, right) //继续右边数组的分治
}
}
recursion(arr)
return arr
}
console.log(quickSort(arr), 'quickSort arr')
优化思考
后面补充
复杂度
最坏时间复杂度:O(n²)
最好时间复杂度:O(nlog)
评价时间复杂度:O(n²)
空间复杂度:O(1)
稳定性:稳定