快速选择能否直接找出数组中第 K 个最大元素?
摘要:本题在线练习:LeetCode 215. 数组中的第K个最大元素 - 在线练习(免费 · 无需登录 · AI 辅助)(https:onefly.topzero2Leetcodepl
本题在线练习:LeetCode 215. 数组中的第K个最大元素 - 在线练习(免费 · 无需登录 · AI 辅助)(https://onefly.top/zero2Leetcode/playground.html?id=215)
配套刷题网站 Zero2Leetcode —— 内置本地 OJ + AI 教练,零门槛开始刷 Hot 100。
题目概述
给定整数数组 nums 和整数 k,返回数组中第 k 个最大的元素。
注意:是“按大小排序后的第 k 个”,不是第 k 个不同的元素。
核心思路:Quickselect(快速选择)只做必要的分区
如果直接排序,时间复杂度是 O(n log n),一定能过,但不是最优。
Quickselect 的思想:
使用“快速排序的 partition 操作”把数组分成两部分
partition 后,某个位置 p 上的元素已经是最终有序数组中的正确位置
如果 p 正好是目标下标,直接返回
否则只在目标所在的那一边继续做 partition
平均时间复杂度 O(n)。
第 k 大转换成“第 n-k 小”(0-based):
target = len(nums) - k
代码实现(Python):随机化 Quickselect(平均 O(n))
import random
from typing import List
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
target = len(nums) - k # 第 k 大 = 第 (n-k) 小(0-based)
def partition(l: int, r: int, pivot_index: int) -> int:
pivot = nums[pivot_index]
nums[pivot_index], nums[r] = nums[r], nums[pivot_index]
store = l
for i in range(l, r):
if nums[i] < pivot:
nums[store], nums[i] = nums[i], nums[store]
store += 1
nums[store], nums[r] = nums[r], nums[store]
return store
l, r = 0, len(nums) - 1
while True:
pivot_index = random.randint(l, r)
p = partition(l, r, pivot_index)
if p == target:
return nums[p]
if p < target:
l = p + 1
else:
r = p - 1
逐行拆解
target = len(nums) - k
把“第 k 大”转为“第 target 小”,这样 partition 用 < pivot 的逻辑就很自然。
if nums[i] < pivot:
swap(...)
partition 的结果是:
[l, store-1] 都 < pivot
store 是 pivot 的最终位置
[store+1, r] 都 >= pivot
if p == target: return nums[p]
一旦 pivot 的最终位置刚好是目标下标,答案就出来了,不必再处理其它元素。
手动模拟 / 举例说明
nums = [3,2,1,5,6,4], k=2
n=6,target = 6-2 = 4,即找排序后下标 4 的元素(也就是第 2 大)
Quickselect 会不断 partition,把正确位置逼出来,最终返回 5
复杂度分析
平均时间复杂度:O(n)(随机 pivot 的期望)
最坏时间复杂度:O(n^2)(极端 pivot 每次都很差),随机化能显著降低这种情况
空间复杂度:O(1) 额外空间(原地 partition)
备选方案:小根堆(更稳定但更慢)
如果希望复杂度更稳定,可以维护大小为 k 的小根堆:
扫描数组,把元素入堆
堆大小超过 k 就弹出最小值
最终堆顶就是第 k 大
时间 O(n log k),空间 O(k)。
总结
这题最推荐掌握 Quickselect:
不用排序全数组
通过不断 partition,把答案位置直接“选出来”
把 target = n - k 这个转换记牢,写起来会非常顺。
