分治算法描述(快速选择找第 k 小)
一、分治算法描述(快速选择找第k小)
1. 自然语言描述
本算法基于快速排序的划分思想,采用分治策略实现平均$(O(n))$复杂度查找第$k$小数:
- 划分(partition)**:在当前数组区间$([left,right])$选取基准元素pivot,将区间划分成两部分:左侧所有元素≤pivot,右侧所有元素≥pivot;划分完成后基准落在其有序数组的正确下标$x$。
- 分治判断
- 若基准下标$x$恰好等于目标下标$k-1$(数组从0开始,第$k$小对应下标$k-1$),基准就是答案,直接返回;
- 若$x<k-1$:第$k$小元素只存在于右区间$([x+1,right])$,递归处理右区间;
- 若$x>k-1$:第$k$小元素只存在于左区间$[left,x-1]$,递归处理左区间。
- 递归边界:区间仅存一个元素($(left==right)$),该元素即为所求。
2. 伪代码
// 划分函数,返回基准最终下标
partition(a[], left, right):
mid = left + (right - left) / 2
pivot = a[mid]
s = 空列表(存≤pivot)
m = 空列表(存>pivot)
for i = left to right:
if i == mid: continue
if a[i] <= pivot: s.append(a[i])
else: m.append(a[i])
// 回填数组
sh = s长度
for i = 0 to sh-1: a[left+i] = s[i]
a[left + sh] = pivot
for j = 0 to m长度-1: a[left+sh+1+j] = m[j]
return left + sh
// 分治查找函数,k_target为目标下标(第k小对应k-1)
find(a[], left, right, k_target):
if left == right:
return a[left]
x = partition(a, left, right)
if x == k_target:
return a[x]
elif x < k_target:
return find(a, x+1, right, k_target)
else:
return find(a, left, x-1, k_target)
// 主流程
输入n, k
输入数组a
res = find(a, 0, n-1, k-1)
输出res
二、时间复杂度分析
- 最好时间复杂度:$(O(n))$
每次划分的基准恰好将区间均分,仅需递归处理一半子区间。总运算量为$n + \frac{n}{2} + \frac{n}{4} + ... ≈ 2n$,线性复杂度$(O(n))$。 - 最坏时间复杂度:$O(n^2)$
每次选取的基准为区间最大/最小值,划分后仅剔除1个元素,递归规模仅减少1。总运算量$n+(n-1)+(n-2)+... = \frac{n(n+1)}{2}$,平方复杂度$O(n^2)$。
三、对分治法的体会与思考
- 分治核心逻辑:分、治、合
分治三步为拆分大问题、递归求解子问题、合并子问题解。本题是特殊分治:无需合并,划分后只需递归求解单侧子区间,直接舍弃另一半,大幅减少计算量,这是快速选择效率优于全排序的关键。 - 划分质量决定算法效率
分治算法性能高度依赖拆分方式。本算法中基准选取直接决定复杂度:均衡划分可达线性时间,极差划分会退化到平方级;随机选取基准可大幅降低最坏情况出现概率。 - 分治的优势:避免冗余计算
若先完整排序再取第$k$小,复杂度为$O(n\log n)$;而快速选择只需要定位目标元素,不需要完成全区间排序,跳过大量无关元素处理,体现分治“缩小问题规模、减少无效运算”的优势。 - 适用场景总结
分治法适合原问题可拆分为结构相同、相互独立的子问题的场景,常通过递归实现。二分查找、快速排序、归并排序、快速选择都是典型分治应用;其中仅需单侧递归的分治算法,通常时间效率更优。

浙公网安备 33010602011771号