算法设计与分析:深入理解快速排序

本文详细解析快速排序算法的原理、时间复杂度,并探讨了如何通过选择pivot来优化效率。通过实例分析,展示了快速排序在不同情况下的时间复杂度,并推导出期望时间复杂度为O(nlogn)。此外,提出了随机选择pivot和多点取中位数等优化策略,以及简单快速排序的Python实现。

本文参考UCAS卜东波老师的计算机算法设计分析课程完成

前言

快速排序是复杂度 O ( n l o g n ) O(nlogn) O(nlogn)的排序算法中最常用的一个,也是分治思想的一个具体应用,关于分治思想的理解可以参考我的这篇文章分治思想。在了解快速排序前,我建议先理解分治思想和归并排序,掌握之后再来看快速排序,会有更好的效果。

快速排序

原理

快速排序与归并排序最明显的不同点是,前者基于数值划分,后者基于下标划分。快速排序会从数组中找一个中间数pivot,将数组小于piovt的放到左边,大于pivot的放到右边。这个貌似很容易理解(先不管pivot如何选取),我们可以得到如下伪代码

quick_sort(A):
	S_l = [],S_r = []  # s_l存储小于pivot的值,s_r存储大于pivot的值
	choose pivot A[p]  # 随机抽取
	for i = 0 to |A|-1:
		if A[i] < A[p]:
			S_l += A[i]  # 若比pivot小,则将A[i]放到pivot左边
		else:
			S_r += A[i] # 否则将A[i]放到pivot右边
	quick_sort(S-)
	quick_sort(S+)
	output S-,A[p],S+  # 按照顺序输出三者

将上面的过程转换成图例如下:
在这里插入图片描述

第一次pivot选择4(随机),213放左边,5放右边,第二次pivot选择3,21放左边,右边为空,第三次pivot选择1,左边为空,2放右边。得到有序数组。

时间复杂度

  • 估算时间复杂度
    上述过程的时间复杂度是多少呢?好像是 O ( n l o g n ) O(nlogn) O(nlogn)?我们考虑两种极端情况,pivot每次都选中了中位数和pivot每次都选了边边的数。两种划分的结果如下:
    在这里插入图片描述
    发现时间相差较大,写出递推公式分别如下(每一次选定pivot,所有数要和其比较,额外开销时间复杂度 O ( n ) O(n) O(n)):
    • 最好情况
      T ( n ) = 2 T ( n 2 ) + O ( n ) = O ( n l o g n ) T(n)=2T(\frac{n}{2})+O(n) = O(nlogn) T(n)=2T(2n)+O(n)=O(nlogn)
    • 最坏情况
      T ( n ) = T ( n − 1 ) + O ( n ) = O ( n 2 ) T(n)=T(n-1)+O(n) = O(n^2) T(n)=T(n1)+O(n)=O(n2)

那么一般情况下是什么样的呢?
假设取pivot的时候使得划分比例是3:1,那么划分得到的树如下所示(也就是分治思想一文中提到的不均匀划分):
在这里插入图片描述
容易知道这棵树的高度是 l o g 4 3 n log_\frac{4}{3}n log34n,由于每一次划分的额外开销是 O ( n ) O(n) O(n)所以总的时间复杂度是 O ( n l o g 4 3 n ) = O ( n l o g n ) O(nlog_\frac{4}{3}n)=O(nlogn) O(nlog34n)=O(nlogn),和最好情况一样的复杂度,同样的,按8:1,100:1的划分也都有 O ( n l o g 9 8 n ) = O ( n l o g 101 100 n ) = O ( n l o g n ) O(nlog_\frac{9}{8}n)=O(nlog_\frac{101}{100}n)=O(nlogn) O(nlog89n)=O(nlog100101n)=O(nlogn)

有人可能不理解这里,为什么划分100:1这么大,仍然复杂度还是 O ( n l o g n ) O(nlogn) O(nlog

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值