题目描述
思路解析动画文字版
排一遍当然行,O(n log n)。但题目只要「第 K 大」一个数,把整个数组都排好太亏了——大量功夫花在了和答案无关的元素上。
快排里 partition 一轮,能让基准归到它在排序结果里的下标 p。我们只关心「第 K 大该在的下标 target=n−k」:若 p=target,基准就是答案;若 p 偏小,目标在右半;偏大则在左半——只递归含目标的那一半,另一半直接扔。这就是快速选择比快排省的地方。
目标:找排序后下标 4 的数:六根柱子。第 2 大=排序后下标 4 的那个数。我们不排序,用 partition 一步步把它逼出来。
第 1 轮分区 · 选末尾 4 当基准:选最后一个数 4 当基准(橙色)。和快排一样,把比 4 小的甩到左边、不小于 4 的留右边。
扫描 · 3、2、1 都小于 4 → 进左区:从左扫:3、2、1 依次都小于 4,全部留在左区(它们本就在前面,原地不动)。接着 5、6 不小于 4,跳过。
基准 4 归位到下标 3:把基准 4 换到小区后面:4 归位到下标 3(绿色)。左边 [3,2,1] 全小于 4、右边 [6,5] 全大于 4。partition 返回 p=3。
p=3 小于 target=4 → 只看右半:基准落在下标 3,比目标下标 4 小——这正是「位置不对」的分支:目标在它右边,所以只递归右半 [6,5],左边那一整段(灰)看都不用看。
第 2 轮分区 · 右半选末尾 5 当基准:只在右半 [6,5] 里继续,选末尾 5 当基准。扫描:6 不小于 5,跳过。
基准 5 归位到下标 4:6 大于 5,5 换到下标 4 归位,6 落到下标 5。partition 返回 p=4。
p=4 == target → 答案 = 5:基准 5 正好落在下标 4(= n−k),它就是第 2 大。返回 5,结束——全程没把整个数组排完,只动了含目标的那一侧。
快排两边都递归(要全排好);快速选择只要找第 K 个,只走含目标的那一边,所以更快。「第 K 大/小」「找中位数」「Top-K」都用这套。
迁移阶梯:把这题练到能复述后,再去同类题里迁移:先复用状态定义,再调整边界条件。
边界三连:边界三连:极端输入先过一遍。
几个高频追问,记住答法。
参考代码
class Solution: def findKthLargest(self, nums, k): target = len(nums) - k # 第 K 大 = 升序下标 n-k l, r = 0, len(nums) - 1 while True: pivot, p = nums[r], l # 选末尾当基准 for i in range(l, r): if nums[i] < pivot: # 比基准小 → 换到左区 nums[i], nums[p] = nums[p], nums[i]; p += 1 nums[p], nums[r] = nums[r], nums[p] # 基准归位到 p if p == target: return nums[p] if p < target: l = p + 1 # 只看右半 else: r = p - 1 # 只看左半复杂度
- 时间复杂度:平均 O(n),只递归一边,规模 n+n/2+n/4+… 约等于 2n;最坏 O(n²)(基准选偏,随机化避免)
- 空间复杂度:O(1),原地分区,迭代写法无递归栈
可套用的代码模板
骨架:target = n−k、partition 拿到归位下标 p、只往 p 偏向 target 的那边收。和快排唯一的差别就是「只递归一边」。
Python
# 快速选择骨架(平均 O(n))target = n - k # 第 K 大 = 升序下标while True: p = partition(l, r) # 基准归位到 p if p == target: return nums[p] elif p < target: l = p + 1 # 只递归目标所在的一半 else: r = p - 1易错点
面试追问把动画讲成自己的话
追问堆和快速选择怎么选?
追问大小 K 的堆用大根还是小根?
追问快速选择最坏怎么办?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
任务调度器
LeetCode 621 · 中等 · 沿着 堆 / 优先队列 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题