(a1)快速排序:算法A

官官方信息技术老师·15 页·深入(追求细节与边界)·0 次浏览·38 天前
排序算法分治思想边界细节

快速排序(算法A)

看完你能徒手推导轴点划分每一步,并说清最坏情形何时发生

按 空格/→ 演示下一步

问一句
1 / 15 页

全部页面点击任意一页,跳回舞台从这页播放

排序算法分治思想边界细节

快速排序(算法A)

看完你能徒手推导轴点划分每一步,并说清最坏情形何时发生

1第 1 页 · 快速排序(算法A)

什么是分而治之

面对一大箱乱放的书,没人会一口气整理完。聪明的做法是:先分堆,各堆各自整理,最后按顺序把堆摞起来——这就是快速排序背后的分而治之思想。

拆小
选一个基准书,按它把书分成两堆:比它矮的、比它高的
各自解决
每堆再重复同样的分法,小问题递归处理
合并结果
快排几乎不用合并,左右堆加基准拼起来即有序
整理一大箱书对应 →分而治之

分堆对应拆小,逐堆整理对应递归求解,摞堆对应合并

2第 2 页 · 什么是分而治之

分治在快排中的映射

从上往下看:数组如何被轴点层层切分,最后为什么不需要合并这一步。

图解渲染中…
b1:划分:一趟扫描把元素按轴点分到两侧d1:子问题有序后拼接即整体有序
3第 3 页 · 分治在快排中的映射

快排的整体思路

上一页我们把数组拆成了「轴点 + 左右两堆」。现在把分治的完整流程串起来:其实快排整件事,就浓缩成三个反复执行的动作。

选轴点
从区间里挑一个基准元素,作为划分的参照
划分两侧
一趟扫描,把小的放轴点左边,大的放右边
递归子区间
对左右两段重复同样动作,直到区间只剩一个元素
整理书堆对应 →快排流程

抽出一本对照书、按厚薄分两边、再分别整理两小堆

4第 4 页 · 快排的整体思路

什么是轴点

上一页说,快排把区间拆成两半的关键,是先找一个「基准」。想想排队时老师挑出一个人,喊一声:比你矮的站左边,比你高的站右边——这个人就是轴点。

基准元素
从区间中选出的参照者,通常取第一个元素
左侧条件
轴点左边的元素都不大于它
右侧条件
轴点右边的元素都不小于它
就位
分区完成后轴点落在最终正确位置,不再移动
排队按个子分边对应 →轴点划分

被挑中的同学对应轴点,左右两队对应不大于与不小于它的两半

5第 5 页 · 什么是轴点

轴点的两个性质

顺着箭头看:轴点归位后,左区全部不大于它,右区全部不小于它。

图解渲染中…
b1:轴点不再移动,位置即它在有序序列中的位置d1:轴点归位后无需再参与递归p2:一次划分把区间切成三部分:左区、轴点、右区
6第 6 页 · 轴点的两个性质

轴点与有序的关系

前面我们认识了轴点的两个性质。现在追问一句:轴点离开位后,自己跑去哪了?答案是——它落在哪,哪就是它的最终位置。这一页我们把这话说清楚。

一越定终身
轴点在划分后所在的位置,就是它在完全有序序列中的位置
为何必然
左边全不大于它,右边全不小于它,换任何有序排列它都在这
正确性根基
轴点不必再参与后续排序,递归只需处理左右两个子序列
与归并对比
归并靠合并保证有序,快排把功夫花在划分前——先安顿轴点
考试按分数排座位对应 →轴点归位

你的分数介于两边同学之间,排完无论怎么排你都在这个座位

7第 7 页 · 轴点与有序的关系

构造轴点的思路

构造轴点分四步:先立候选,再逐个比较、各归两侧,最后落位。

1
取首元素为候选
暂存首元素腾出空位,它就是轴点候选
2
从后端补位
尾部小于候选的元素填入前端空位
3
从前端补位
前端不小于候选的元素填入尾部空位
4
候选落位
两端相遇处放入候选,轴点构造完成
8第 8 页 · 构造轴点的思路

swap 与移动的代码骨架

python

用一段真实可运行的 Python,看清比较、交换、轴点归位三件事的最小骨架。

代码高亮加载中…

partition 用'挖坑填坑'替代反复 swap:j 找小者填左坑,i 找大者填右坑,最后 pivot 归位,正好满足轴点的两个性质。

9第 9 页 · swap 与移动的代码骨架

单调性:查找交换的节奏

上一页搭好了 swap 骨架,但还差一个关键问题:往哪儿搬?答案是两个哨兵从两头向中间扫——右边的找小的,左边的找大的,每换一次,可行区间就缩一截。

右哨兵先动
从右往左找第一个比轴点小的元素,停在那里
左哨兵后动
从左往右找第一个比轴点大的元素,找到就交换
交替扫描
两边轮流出发,像拉锯一样反复找小、找大
区间单调收缩
每次交换后待定范围只减不增,相遇即结束
两队相向巡逻对应 →双哨兵扫描

两队各自清查半条街,清一段少一段,最终碰头

10第 10 页 · 单调性:查找交换的节奏

不变性:为什么不会出错

沿着箭头看:每一轮交换后,左右两区的不变式依然成立,直到扫描结束轴点归位。

图解渲染中…
b1:已扫左侧区域,所有元素≤轴点候选pc1:已扫右侧区域,所有元素≥pd1:每轮swap只在未扫区内部进行g1:递归后各区有序,拼起来即全局有序
11第 11 页 · 不变性:为什么不会出错

实例:完整走一遍

用序列 5 3 8 1 9 2 走一遍划分,轴点 5 逐步归位。

1
初始化
取首元素 5 为轴点备份,L 从左、R 从右出发,5 的位置视为空穴
2
R 向左找小
R 扫到 2 小于 5,把它填入左侧空穴,R 原位变成新空穴
3
L 向右找大
L 扫到 8 大于 5,填入右侧空穴;L 继续右移,8 处变空穴
4
R 再找小
9 大于 5 跳过,1 小于 5,填入左侧空穴,1 处变空穴
5
相遇归位
L 与 R 相遇,空穴处即轴点最终位置,把 5 放入,划分完成
12第 12 页 · 实例:完整走一遍

自测:轴点归位

点击作答

对序列 [4,1,3,5,2] 做一轮划分(以 4 为轴点,构造完成后),轴点 4 位于哪个位置(下标从 0 起)?

13第 13 页 · 自测:轴点归位

要点收束

  • ✓分治三问——切、治、合,快排的「合」是免费的
  • ✓轴点归位:左小右大,位置即最终位置
  • ✓单调性保证交换不漏不重,一个元素一回合
  • ✓不变性是循环正确性的证明骨架
  • ✓复杂度由轴点是否均衡决定
延伸主题:最坏与平均复杂度分析随机化轴点与三数取中算法B:两端交替的分区法
14第 14 页 · 要点收束

课后思考

先合上笔记自己想,再看参考答案——这些问题没有唯一解。

1为什么快排常见实现偏爱取端点或随机位置做轴点?取中间元素会怎样?

参考答案从成本与效果权衡:随机化让最坏情况难以被针对构造;端点实现最简单。取中间元素完全可行,某些有序输入上反而更均匀,可对照单调性一页验证。

2如果数组里所有元素都相等,算法A的每次划分会发生什么?这暴露了什么短板?

参考答案每次划分都极度偏斜,递归深度退化到 O(n),总时间 O(n²)。提示:可从查找交换的节奏入手,思考如何让相等元素分摊到两侧。

3划分中的不变式思路,能否迁移到「把奇数移到偶数前面」这类重排问题上?

参考答案可以。找出你希望始终为真的性质(如前段全是奇数),再用双指针维护它。这正是不变式方法的价值:先定性质,再推操作。

15第 15 页 · 课后思考