理解:
- 在调用分区函数之前,默认每次选择的比较标准都是某段数组的最后一个位置的值,这个比较标准越靠近这段数组的中位数,时间复杂度越低,否则时间复杂度越高。因此在这段数组中随机选取一个数与最后一个数交换,概率降低时间复杂度。
- 分区函数可以根据比较标准将某段数组分成较小数区域靠前,相等数区域在中间,较大数区域靠后,那么此时可以确定相等数的位置就是整个数组有序后的位置,相等数的位置也就固定下来了,并返回相等数区域的左右边界。
- 递归函数不断调用分区函数可以得到相等数区域的左右边界,以此得出较小数区域和较大数区域,继续递归较小数区域和较大数区域,就可以将每个相等数位置固定,最终让整个数组有序。
代码:
function quickSort(arr) {
// 不用处理的数组直接返回
if (arr == null || arr.length < 2) {
return
}
// 开始递归 递归整个数组
recursion(arr, 0, arr.length - 1)
}
// ------------------------4个函数-----------------------------------
// ------------------------4个函数-----------------------------------
// 递归函数 递归数组中的某一段
// 参数是数组某段的左右边界 arr[l...r]
function recursion(arr, l, r) {
// 防止溢出???
if (l < r) {
// 固定选择每段数组的最后一个数作为比较标准 这个数越趋近于数组的中位数 时间复杂度越低
// 因此随机选取某段数组arr[l...r]中的一个数与最后一个数进行交换 概率降低时间复杂度
let ranInt = parseInt(Math.random()*(r - l + 1) + l)
swap(arr, ranInt, r)
// 将某段数组进行分区后返回其中相等数的左右边界
let p = partition(arr, l, r)
// 递归较小数区域
recursion(arr, l, p[0] - 1)
// 递归较大数区域
recursion(arr, p[1] + 1, r)
}
}
// ------------------------4个函数-----------------------------------
// ------------------------4个函数-----------------------------------
// 分区函数 将某段数组进行分区
// 参数是这段数组的左右边界arr[r...l]
// 这段数组按照比较标准进行分区后 较小数在前 相等数在中间 较大数在后
// arr[<...<, =...=, >...>]
// 此时可以确定的是 相等数的位置就是整个数组有序后的位置
// 返回相等数的左右边界[=,=]
function partition(arr, l, r) {
// 较小数和较大数区域的指针
let low = l - 1, high = r
// 此时左边界指针被当做查数指针 当前数指针
// 右边界指针被当做标准数指针 保持不动
// 按照这样的结构修改数组 arr[low...low,=...=,l.........,high...high,r]
while (l < high) {
// 当前数小于标准
if (arr[l] < arr[r]) {
// 当前数进入较小数区域
// 较小数区域向右推进 此时当前数指针指向了一个相等数,直接右移
swap(arr, l++, ++low)
// 当前数大于标准
}else if (arr[l] > arr[r]) {
// 当前数进入较大数区域
// 较大数区域向左扩展 此时当前数指针指向了一个未查看的数,不能右移,进入下一个循环
swap(arr, l, --high)
}else{
// 当前数等于标准
// 当前数指针直接右移
l++
}
}
// 将标准数与较大数区域的最前面一个数交换
// arr[low...low,=...=,high...high,r]
swap(arr, high, r)
// 返回相等数的左右边界[=,=]
return [low + 1, high]
}
// ------------------------4个函数-----------------------------------
// ------------------------4个函数-----------------------------------
function swap(arr, i, j) {
let tmp = arr[i]
arr[i] = arr[j]
arr[j] = tmp
}
性能:
- 时间复杂度:长期期望是O(N*log N)
- 空间复杂度:O(log N)
- 稳定性:无法保持,分区函数直接交换导致无法保持稳定性