快速排序的最坏时间复杂度为θ(n2),但是它的平均性能很好,它的期望时间复杂度为θ(nlgn)
QUICKSORT(A,p,r)
实现了对子数组A[p..r]的原址重排
QUICKSORT(A,p,r)
QUICKSORT(A,p,r)
if p < r
q = PARTITION(A,p,r)
QUICKSORT(A,p,q-1)
QUICKSORT(A,q+1,r)
对于A[q]的疑问:我们人为的把数组拆分为A[p,q-1]和A[q+1,r]中的每一个元素,使得A[p,q-1]中的每个元素都小于等于A[q],而A[q+1,r]中的每个元素都大于等于A[q]中的每一个元素。而这一目的在我们调用PARTITION(A,p,r)
获取q时就已经达到。
PARTITION(A,p,r)
PARTITION(A,p,r)
x = A[r] # 作为主元
i = p - 1
for j = p to r - 1
if A[j] <= x
i = i + 1
exchange A[i] with A[j]
exchange A[i + 1] with A[r]
return i + 1
A:为要排序的目标数组;p、r表示要排序数组的下标范围为[p,r]
在每次执行完PARTITION(A,p,r)后,在循环体的下一轮迭代的开始时,对于任意的数组下标k,有:
若p<=k<=i,则A[k]<=x
若i+1<=k<=j-1,则A[k]>=x
若k==r,则A[k]=x
上面的三种排序后的情况中没有覆盖j<=k<=r-1的情况,在此情况下,元素没有特定的大小关系。也就说PARTITION(A,p,r)函数每次都只专注于A中的指定范围内元素的排序,而对于本次排序范围之外的元素的大小关系不必考虑在内,那是其他时刻需要做的事情。
修改为非递增序
PARTITION(A,p,r)
PARITITION(A,p,r)
x = A[r]
i = p - 1
for j = p to r - 1
do if A[j] >= x
then i = i + 1
exchange A[i] with A[j]
exchange A[i + 1] with A[r]
return i + 1
平衡与不平衡的划分
不平衡的划分
极端情况下,每次划分产生的两个子问题分别包含了n-1个元素和0个元素时,快速排序的最坏情况发生
平衡的划分
只要划分是常数比例的,算法的运行时间总是O(nlgn)
快速排序的随机化版本
使用随机化版本的快速排序作为大数据输入情况下的排序算法
通过对序列p,…,r的随机抽样,我们可以保证主元元素x=A[r]是等概率地从子数组的r-p+1个元素中选取的
RANDOMIZED-PARTITION(A,p,r)
RANDOMIZED-PARTITION(A,p,r)
i = RANDOM(p,r)
exchange A[i] with A[r]
return PARTITION(A,p,r)
RANDOMIZED-QUICKSOR(A,p,r)
RANDOMIZED-QUICKSOR(A,p,r)
if p < r
q = RANDOMIZED-PARTITION(A,p,r)
RANDOMIZED-QUICKSOR(A,p,q-1)
RANDOMIZED-QUICKSOR(A,q+1,r)
拓展
什么是原址排序?
在排序算法中,如果输入数组中仅有常数个元素需要在排序过程中存储在数组之外,则称排序算法是原址的。插入排序、堆排序、快速排序等都是原址排序。归并排序不是原址的。