数组的Partition与快速排序
数组的Partition
在数组中取一个数p,把<=p的数都放在左边,>p的数放在右边,数的排序可以是无序的。
流程:
在a[0]之前建一个<=区,i指向a[0]
若a[i] <= p, a[i]与<=区的右边一个数交换,<=区向右扩一个单位,i++
若a[i] > p, i++
升级:荷兰国旗问题
在数组中取一个数p,把<p的数都放在左边,=p的数放中间,>p的数放在右边,数的排序可以是无序的。
流程:
在a[0]之前建一个<=区,i指向a[0],a[a.length-1]之后建立一个>=区
若a[i] < p, a[i] < p, a[i]与<区后一个数交换,<区向右扩,i++
若a[i] > p,a[i]和>区前一个数交换,>区向左扩,i不变
若a[i] == p,i++
快速排序
快速排序的核心思想便是数组的partition,通过一轮partition之后,可以把一个数插到中间,这样每个数都会找到自己的位置,然后把左边和右边的数组分别递归进行partition,每个数字都可以找到自己的位置,最后数组便是有序的了。
没有任何改进的快排
用数组的最后一个数p作为划分,<=p的放数组的左边,>p的放数组的右边,一轮partition之后,p就存在与数组中间的某个位置,然后p不动,p左边的数组和右边的数组重复以上流程。
稍微改进的快排
用数组的最后一个数p作为划分,把<p的数都放在左边,=p的数放中间,>p的数放在右边,然后=p的区域不动,区域左边的数组和区域右边的数组继续重复以上流程。
进阶改进的快排
每次都随机把l到r上的任意一个数交换到最后,然后用这个数进行partition,最后p落在任意一个位置上的概率都是相等的,即1/n
累加的公式得出来,时间复杂度为O(n*logN),最坏情况和最好情况都已成概率事件了
工程上改进的快排
在quickSort这一步,当l < r - 60 时,使用插入排序。虽然插入排序时间复杂度为O(n^2),但是样本量少的时候速度飞快,没有时间瓶颈。
快速排序的时间复杂度分析
1.最坏O(n^2)
例子 1 2 3 4 5 6 7 8 9
以9作为划分,每次只搞定了最后一个数,每次都要承载O(n)的时间复杂度,所以总的时间复杂度为O(n^2)
2.最好O(n*logN)
每次都把p打到中点位置,左边和右边的长度都为n / 2
得出来的公式:T(N) = 2T(N / 2) + O(N)
其中O(N)为partition的时间复杂度
根据master公式得出时间复杂度为O(n)
由于按最差情况来估计时间复杂度,所以完全不改进的快排时间复杂度为O(n^2)。
空间复杂度
1.最坏情况 O(N)
2.最好情况 O(logN)
结论:经过概率的统计,最后收敛于O(logN)。
快速排序的Code
public class QuickSort_lijunru {
public static void quickSort(int[] arr) {
if (arr == null || arr.length < 1) {
return;
}
qui