快速排序

// 快速排序
/*
思路

在数组中找一个基准数(假设以数组第一个元素当基准)
将比基准数大的放到右边,比基准数小的放到他左边,最后数组一分为二,左边都比这个数大,右边都比这个数小
然后在这两个数组中再分别去操作


具体操作是将第一个元素设为基准数,从右边最后一个元素开始找,找小于等于基准数的,停下来,然后从左边第一个元素开始找,
找大于等于基准数的,停下来,然后进行交换,直到右边的和左边的指向同一个数,然后将这个数与基准数交换,此时这个数左边都比基准数大,
右边都比基准数小。




为什么要从右边开始找? 因为找到后要进行交换,从右边找到更小的,与基准交换后能确定更小的在左边更大的在右边



时间复杂度


最坏情况:
假设数组 [1,2,3,4,5] 这样一个有序的数组,基准数是1,
一开始从右边找小于等于1的,找到1本身,然后分为[1]和 [2,3,4,5]
一共执行了 n+ n-1 +n-2 +...1 = (1+n)*n/2 = 1/n + 1/n2 时间复杂度是 o(n2)

(来看最糟糕情况下的快排,当待排序的序列为正序或逆序排列时,且每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。)


最好情况:
假设每次第一个元素都是当前数组的中间值
那么第一次划分2个,第二次划分四个,第三次划分8个,
需要划分log2n次 (个数为n)

假设每次都遍历数组所有元素,所有的遍历次数是nlogn


*/



let arr = [3, 4, 5, 2, 1, 7, 2, 11]

function quicksort(arr, left, right) {

let pivot = arr[left]
let j = right
let i = left

// 退出条件
if (left >= right) return

while (i < j) {
while (arr[j] >= pivot && i < j) {
j--
}
while (arr[i] <= pivot && i < j) {
i++
}
// 现在找到了,要进行交换
if (i < j) {
let tmp = arr[j]
arr[j] = arr[i]
arr[i] = tmp
}
}

// 现在要把它和pivot交换
arr[left] = arr[i]
arr[i] = pivot

//交换完后在下一个数组分组里分治
quicksort(arr, left, i - 1)
quicksort(arr, i + 1, right)
}

quicksort(arr, 0, arr.length - 1)

console.log(arr);


========================================
 
 
 
最坏情况与最好情况:
 

最坏时间复杂度 o(n2)

当数组本身是一个有序数组时,如果以第一个元素作为pivot(枢纽、基准)

那么,每次分的时候,都是一边一个,另一边n-1个

需要遍历 n + n-1 + n-2 + n-3 +... + ... 1

即1+2+3+...n = (上底+下底)*高/2 = (1+n)n/2 = O(n2)

最好时间复杂度 o(nlogn)

最好的情况是每次都能均匀的划分序列 

如果第一个元素作为pivot ,每次这个pivot恰好是数组的 中间值

那么会分成左右两边 设每层需要的时间最坏是n(也就是所有的遍历一遍),

下面看一下每次分层后,这个一共有多少层,

第一层是一个数组(2^0),第二层是2个数组(2^1),第三层是4个数组(2^2),第四层是8个数组(2^3)

... 到最后一层会有n个数组, 那么一共会有  (2^x = n ; x = logn)层               

注: log2n 简记为 logn 因为https://www.cnblogs.com/eret9616/p/12929769.html

所以最坏总遍历次数是层数*循环的列为 logn*n     即为 nlogn

  

平均时间复杂度 O(nlogn)

这个需要一些数学工具,推理过程比较复杂,记住结论就行

复杂度图像

 
原文地址:https://www.cnblogs.com/eret9616/p/9874559.html