算法复习_快速排序

快速排序

基础快排实现

一种允许就地排序并且通常快于合并排序的递归算法。

在数组中寻找一个标记(pivot)将数组分成两部分

平均情况:时间复杂度 O ( l o g n ) O(logn) O(logn),空间复杂度 O ( l o g n ) O(log n) O(logn)

最坏情况:时间复杂度 O ( n 2 ) O(n^2) O(n2),空间复杂度 O ( n ) O(n) O(n)

划分过程简单实现

PARTITION(A,p,r)
	x <- A[r] //pivot
	i <- p-1
for j <- p to r-1
	do if A[j] <= x
		then i <- i+1
			exchange A[i] and A[j]
exchange A[i+1] and A[j]
return i+1

在这里插入图片描述

给出划分过程:

在这里插入图片描述

图中轻阴影部分表示小于pivot,重阴影部分大于pivot

快排实现(递归)

QUICKSORT(A,p,r)
	IF p<r
	THEN q <- PARTITION(A,p,r) //返回标签index
		QUICKSORT(A,p,q-1) //分治思想
		QUICKSORT(A,q+1,r)

运行时间分析

最好的情况下,列表每次被划分成大致相等的两个部分,因此运行时间和合并排序相似( Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)

最好情况下的递归表达式: T ( n ) = 2 T ( n / 2 ) + Θ ( n ) T(n) = 2T(n/2) + \Theta(n) T(n)=2T(n/2)+Θ(n)

说明: Θ ( n ) \Theta(n) Θ(n)指的是PARTITION的时间代价。如何求解出具体的时间复杂度可以用递归树求解并通过替换法证明。第一章写过在此不再赘述。

最坏的情况下,设想如果我们每次很不幸的选择了最小或最大的元素来作为我们的标记值,那么我们每次都将递归排序比上一次长度-1的序列。

最坏情况下的递归表达式: T ( n ) = T ( n − 1 ) + Θ ( n ) T(n)=T(n-1)+\Theta(n) T(n)=T(n1)+Θ(n),求解为 Θ ( n 2 ) \Theta(n^2) Θ(n2)

如果我们假设每次pivot将序列划分为1:9两个部分,同样可以得到时间复杂度为 Θ ( n l o g n ) \Theta(nlogn) Θ(nlogn)

通过中位数提升快排

  • 如果我们将中位数作为我们的pivot那么它的划分效果将是最为完美的,但是寻找中位数显得非常困难。所以我们将数组头部、尾部、中间下标的中位数作为pivot。
  • 对于小型的数组采用插入排序。

ex:找到数组中从小到大第k个元素

算法1:直接排序+遍历到第k个 时间复杂度: Θ ( n l o g n ) \Theta (nlogn) Θ(nlogn)

算法2:顺序选择: O ( k n ) O(kn) O(kn)

算法3:快速搜索

QuickSearch(A,p,r,k)
	if (p==r) return p
	t <- Partition(A,p,r) //pivot
	if (t==k) return t //pivot所在的位置是他从小到大排列正确的下标
	if (k<t)
		QUICKSearch(A,p,t-1,k) //第k个比pivot小
	else
		QUICKSearch(A,t+1,r,k)

时间复杂度:

最差情况: O ( n 2 ) O(n^2) O(n2) 额外空间: O ( n ) O(n) O(n)

最好情况: O ( 1 ) O(1) O(1)

平均情况: O ( n ) O(n) O(n),额外空间: O ( l o g n ) O(logn) O(logn)

三种排序的比较

在这里插入图片描述

注意:很多人会好奇,快排在原地排序为什么需要额外的空间,这里的空间指的是递归调用时用来保存原来状态的栈空间,即你画的递归树的高度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值