ps: 如果有任何问题可以评论留言,我看到后会及时解答,评论或关注,您的鼓励是我分享的最大动力
转载请注明出处:
https://blog.csdn.net/qq_40938301/article/details/88365408
介绍:
快速排序是历史上最著名的算法之一。1959年由 托尼 霍尔 (Tony Hoare)发明。
目标:
将一个数组按照由低到高(或者由高到低)的顺序排序。
核心思想:
1、基准:在数组中选一个数,作为基准(pivot)
2、分区:将数组根据基准分为三部分(即三个数组):
less:数组中所有数小于基准
equal:数组中所有数等于基准
high:数组中所有数大于基准
三者组合就是分区上的升序,再在3中对分区升序细化至每个元素
3、递归:对 less 和 high 递归做2中的分区,直到数组中只有一个元素不能再分
实现:
快排根据分区的实现方法和基准的选择不同,有不同的版本,性能也有所不同
1、Kotlin (最容易理解的实现版本)
function quickSort(arr){
if(arr.length<2) return arr;
var mid = arr[Math.floor(arr.length/2)];
var less = arr.filter((x)=>x<mid);
var equal = arr.filter((x)=>x==mid);
var high = arr.filter((x)=>x>mid);
return quickSort(less)+equal+quickSort(high);
}
2、Lomuto 分区方案
function partitionLomuto(arr, low, high){
var pivot = arr[high];
var i = low;
for(j=low;j< high;j++){
if(arr[j] <= pivot){
swap(arr,i,j);
i++;
}
}
swap(arr,i,high);
return i;
}
function swap(arr,x,y){
var temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
function quickSortLomuto(arr,low,high){
if(low < high){
var p = partitionLomuto(arr,low,high);
quickSortLomuto(arr, low, p-1);
quickSortLomuto(arr, p+1, high);
}
}
quickSortLomuto(arr,0,arr.length-1);
3、Hoare 分区方案
function partitionHoare(arr, low, high){
var pivot = arr[low];
var i = low-1;
var j = high+1;
while(true){
do{
j--;
}while(arr[j] > pivot);
do{
i++;
}while(arr[i] < pivot);
if(i<j){
swap(arr,i,j);
}
else{
return j;
}
}
}
function quickSortHoare(arr,low,high){
if(low < high){
var p = partitionHoare(arr,low,high);
quickSortLomuto(arr, low, p);
quickSortLomuto(arr, p+1, high);
}
}
分析:
快速排序的性能,与基准的选择以及具体的数组有关
如果选择了好的基准,平均的时间复杂度在 O(n log n)
如果选择了糟糕的基准,时间复杂度会降至 O(n^2)
平均的时间复杂度也是 O(n log n)
快速排序算法不是一个稳定的算法
证明:
1、最优情况
在最优情况下,Partition每次都划分得很均匀,如果排序n个关键字,其递归树的深度就为 [log2n]+1( [x] 表示不大于 x 的最大整数),即仅需递归 log2n 次,需要时间为T(n)的话,第一次Partiation应该是需要对整个数组扫描一遍,做n次比较。然后,获得的枢轴将数组一分为二,那么各自还需要T(n/2)的时间(注意是最好情况,所以平分两半)。于是不断地划分下去,就有了下面的不等式推断:
在最优的情况下,快速排序算法的时间复杂度为O(nlogn)。
2、最坏情况
然后再来看最坏情况下的快排,当待排序的序列为正序或逆序排列时,且每次划分只得到一个比上一次划分少一个记录的子序列,注意另一个为空。如果递归树画出来,它就是一棵斜树。此时需要执行n‐1次递归调用,且第i次划分需要经过n‐i次关键字的比较才能找到第i个记录,也就是枢轴的位置,因此比较次数为
最终其时间复杂度为O(n^2)。
3、一般情况
最后来看一下一般情况,平均的情况,设枢轴的关键字应该在第k的位置(1≤k≤n),那么:
算法导论中的证明: