快速排序
有了荷兰国旗问题这个前置知识,快排就变得很简单了
此文章所使用的快排为:随机快排
快速排序是基于 “分治法” 原理实现。
快排实际上就是不断递归,将数组不断的分为左右两个子过程(中间等于 target 的部分则不用继续排序,相当于是一个小的加速过程),直至将数组切割至长度为 1 或 2 再进行 partition,就完成了一次最小规模的排序,也称为 base case。
代码部分
public class 快速排序 {
public static void main(String[] args) {
int[] arr = {5,2,4,8,9,6,2,5,3,2,1,4,7,8,5,3,6};
quickSort(arr,0,arr.length-1);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
public static void quickSort(int[] arr,int L,int R) {
if (null == arr && arr.length <= 1) {
return;
}
if (L <= R) {
/**
* 完全随机值,避免了人为干扰,复杂度遵循概率统计规律,为 N * logN(以 2 为底)
*/
swap(arr,L + (int) (Math.random() * (R - L + 1)), R); //给定范围的随机数
int[] p = partition(arr,L,R);
quickSort(arr, L, p[0] - 1); // 小于区域
quickSort(arr,p[1] + 1,R); // 大于区域
}
}
/**
* @param R 随机挑选出来的基准数,作为荷兰国旗问题的 target值
* @return int[] 数组包含两个数:划分值相等的左边界和右边界下标
*/
public static int[] partition(int[] arr,int L,int R) {
int less = L-1;
int more = R+1;
//int i = 0; // 不能用这个来代替数组下标了,要用 L 来代替,因为不一定都是从0 开始的了
while (L < more) { // L 表示当前数
if (arr[L] < arr[R]) { // 当前数 < arr[R](target值)
swap(arr,++less,L++);
} else if (arr[L] == arr[R]) { // 当前数 = target值
L++;
} else { // 当前数 > target值
swap(arr,--more,L);
}
}
return new int[] {less+1, more-1}; // target相等区域的左边界、右边界下标
}
public static void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
时间复杂度
n * log n
空间复杂度
n * log n
排序稳定性
因为 partition 过程中的不断交换的缘故,导致可能会将相同数字的相对次序打乱
快排算法的优缺点
优点:
① 随机快排避免了人为构造的最差情况 O(N²),将时间复杂度转化为具有概率统计规律的 O(n * ㏒ n)
② 排序效率很高
缺点:
① 不能保证排序的稳定性
② 小数据量时性能不是很优秀