上篇文章介绍了归并排序,这篇文章用归并排序来改写一下快速排序。同时引入荷兰国旗典型案例。
快速排序
Partition过程
给定一个数组arr,和一个整数num。请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
要求额外空间复杂度O(1),时间复杂度O(N)
- [ 荷兰国旗问题 ]
“荷兰国旗问题” 是计算机科学中的一个经典题目,它是由Edsger Dijkstra提出的。荷兰国旗由红、白、蓝三色组成。题目:现在有若干个红、白、蓝三种颜色的球随机排列成一条直线。现在我们的任务是把这些球按照红、白、蓝排序。
这个问题之所以叫荷兰国旗,是因为我们可以将红白蓝三色小球想象成条状物,有序排列后正好组成荷兰国旗。
快速排序1.0
在arr[L..R]范围上,进行快速排序的过程:
1)用arr[R]对该范围做partition,<= arr[R]的数在左部分并且保证arr[R]最后来到左部分
的最后一个位置,记为M; <= arr[R]的数在右部分(arr[M+1..R])
2)对arr[L..M-1]进行快速排序(递归)
3)对arr[M+1..R]进行快速排序(递归)
因为每一次partition都会搞定一个数的位置且不会再变动,所以排序能完成
快速排序2.0
关键点:当前数如果小于目标数,则小于区域下一个位置和当前数交换;小于区域向右扩一位。(小数像区域靠拢)
在arr[L..R]范围上,进行快速排序的过程:
1)用arr[R]对该范围做partition,< arr[R]的数在左部分,== arr[R]的数中间,
>arr[R]的数在右部分。假设== arr[R]的数所在范围是[a,b]
2)对arr[L..a-1]进行快速排序(递归)
3)对arr[b+1..R]进行快速排序(递归)
因为每一次partition都会搞定一批数的位置且不会再变动,所以排序能完成
快速排序1.0和2.0的时间复杂度分析
数组已经有序的时候就是复杂度最高的时候
时间复杂度O(N^2)
快速排序3.0(随机快排+荷兰国旗技巧优化)
重点是 : 在这个范围上,随机选一个数记为num(不是2.0里的最后一个数)
在arr[L..R]范围上,进行快速排序的过程:
1)在这个范围上,随机选一个数记为num,
2)用num对该范围做partition,< num的数在左部分,== num的数中间,>num的数在右部分。
假设== num的数所在范围是[a,b]
3)对arr[L..a-1]进行快速排序(递归)
4)对arr[b+1..R]进行快速排序(递归)
因为每一次partition都会搞定一批数的位置且不会再变动,所以排序能完成
随机快排的时间复杂度分析
1)通过分析知道,划分值越靠近中间,性能越好;越靠近两边,性能越差
2)随机选一个数进行划分的目的就是让好情况和差情况都变成概率事件
3)把每一种情况都列出来,会有每种情况下的时间复杂度,但概率都是1/N
4)那么所有情况都考虑,时间复杂度就是这种概率模型下的长期期望。
时间复杂度O(N* logN),额外空间复杂度O(logN) 都是这么来的。
例题及变种:
327.[ 区间和达标的子数组数量 ] [H]
[ 快速排序 ]
[ 荷兰国旗问题,分出小于等于的 和大于两部分 ]
eg: 归并排序改写快速排序
public static void sort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
process(arr, 0, arr.length - 1);
}
private static void process(int[] arr, int L, int R) {
if (L >= R) {
return ;
}
swap(arr, (int) (L + Math.random() * (R - L)), R);
//左 小于区域的最边上的位置
//右 大于区域的最边上的位置
int[] areaIndex = range(arr, L, R);
process(arr, L, areaIndex[0]);
process(arr, areaIndex[1] + 1, R);
}
//返回相等区域的左右边界 并划分界限
private static int[] range(int[] arr, int l, int r) {
if (l > r) {
return new int[]{-1, -1};
}
if (l == r) {
return new int[]{l, r};
}
int less = l - 1;
int more = r;
int i =l;
//fix 不能和右边界碰到 不用到R
while (i<more ){
if(arr[i]==arr[r]){
i++;
}else if(arr[i]<arr[r] ){
swap(arr,i++,++less);
}else {
swap(arr,i,--more);
}
}
//之后一步需要进行换
swap(arr,r,more);
return new int[]{less+1,more};
}
private static void swap(int[] arr, int x, int y) {
int tmp = arr[x];
arr[x] = y;
arr[y] = tmp;
}
本周打卡记录
第二周进度: 应刷 14 道,实刷 5道
投入时间 7h+
327.[ 区间和达标的子数组数量 ] [H] 打卡
2022-04-28
[ 快速排序 ] 打卡
2022-04-29
[ 荷兰国旗问题,分出小于等于的 和大于两部分 ] 打卡
2022-04-30
[ 荷兰国旗问题,分出小于的,等于的和大于三部分 递归] 打卡
2022-05-01
[ 荷兰国旗问题,分出小于的,等于的和大于三部分 非递归] 打卡
2022-05-01