经典快排
快速排序是选定数组最后一个值作为标准值,小于等于该值的数移动到数组左侧,大于该值的数移动到数组右侧.递归执行以上步骤.
public class QuickSort {
/**
* 快速排序是选定数组最后一个值作为标准值,小于等于该值的数移动到数组左侧,大于该值的数移动到数组右侧.
* 递归执行以上步骤.
*/
public static void quickSort(int [] arr , int l, int r) {
if (arr.length < 2 && arr == null){ return; }
if(l < r){
int [] p = partition(arr,l,r);
quickSort(arr,l,p[0]);
quickSort(arr,p[1], r);
}
}
/**
* @param arr 给定的数组(区域)
* @param l 左边界
* @param r 右边界
*/
public static int [] partition(int [] arr, int l , int r){
int less = l - 1;
int more = r + 1 ;
int target = arr[r];
for (int i = 0; i < r ; i++){
if (l + i >= more ) { break; }
if (arr[l + i] < target){
swap(arr,l + i,++less);
}else if (arr[l + i] >target){
swap(arr,l + i-- ,--more);
}
}
return new int [] {less , more};
}
}
最优时间复杂度: O(N * log N)
最差时间复杂度:O(N^2)
经典快排存在的问题:
当我们要排序的数组arr是有序的:{1,2,3,4,5},此时按照经典快排的逻辑,选择最后一个元素作为target值,这样导致了小于target的区域元素过多而大于target区域没有元素.每一轮递归排序只能搞定一个值,快速排序的时间复杂复杂度变为了O(N^2).
解决的办法就是随机选择一个值作为排序的target,这样target值在划分的时候,有可能出现最好的情况,也有可能出现最坏的情况.此时排序算法的时间复杂度就掺杂了概率的问题,这种时间复杂度需要使用长期期望的值作为时间复杂度,最终的推导结果是O(N * log N).
该方法叫做随机快排.
随机快排
随机快排的实现就是在partition划分之前,随机选一个元素放到排序区域的最后作为排序的target.如下第四行代码.
随机快排是最常用的.
public static void quickSort(int [] arr , int l, int r) {
if (arr.length < 2 && arr == null){ return; }
if(l < r){
//随机选一个元素放到排序区域的最后
swap(arr, l + (int) (Math.random() * (r - l + 1)), r);
int [] p = partition(arr,l,r);
quickSort(arr,l,p[0]);
quickSort(arr,p[1], r);
}
}
随机快排的时间复杂度: O(N * log N) -- 长期期望
额外空间复杂度: O(log N) --长期期望
随机快排的空间复杂度主要消耗在断点的存储上.具体存储多少断点,也同数据情况有关.
当断点选择比较好,大于断点数据和小于断点数据数量一样时,此时的额外空间复杂度是O(log N)当断点选择成了最大值或最小值这种最差情况时,需要的断点数为O(N)
因为随机快排的概率问题,此时计算出的空间复杂度长期期望为: O(log N).