快速选择算法(Quickselect)
字数 724 2025-11-11 15:06:50
快速选择算法(Quickselect)
快速选择算法是一种用于在未排序数组中找到第k小(或第k大)元素的高效算法。它是快速排序算法的一个变种,平均时间复杂度为O(n),最坏情况下为O(n²),但通过随机化可以优化到平均O(n)。
算法描述
快速选择基于分治思想,通过一次划分操作将数组分为两部分,然后根据目标元素的位置决定递归处理哪一部分,从而避免了对整个数组排序。
算法步骤详解
-
选择基准值(Pivot)
- 从当前子数组中随机选择一个元素作为基准值
- 随机选择可以避免最坏情况的发生
-
划分操作(Partitioning)
- 将数组重新排列,使得所有小于基准值的元素位于其左侧,所有大于基准值的元素位于其右侧
- 基准值最终位于其正确的位置(即排序后的位置)
-
递归选择
- 设基准值的最终位置为p
- 如果p = k-1,则基准值就是第k小的元素
- 如果p > k-1,说明第k小元素在左侧子数组,递归处理左半部分
- 如果p < k-1,说明第k小元素在右侧子数组,递归处理右半部分
具体实现过程
import random
def quickselect(arr, k):
"""
在数组arr中寻找第k小的元素(k从0开始计数)
"""
def select(l, r, k_index):
if l == r: # 只有一个元素
return arr[l]
# 随机选择基准值
pivot_index = random.randint(l, r)
# 执行划分,将基准值放到正确位置
pivot_index = partition(l, r, pivot_index)
if k_index == pivot_index:
return arr[k_index]
elif k_index < pivot_index:
return select(l, pivot_index - 1, k_index)
else:
return select(pivot_index + 1, r, k_index)
def partition(l, r, pivot_index):
"""划分函数,返回基准值的最终位置"""
pivot_value = arr[pivot_index]
# 将基准值移到末尾
arr[pivot_index], arr[r] = arr[r], arr[pivot_index]
store_index = l
for i in range(l, r):
if arr[i] < pivot_value:
arr[store_index], arr[i] = arr[i], arr[store_index]
store_index += 1
# 将基准值放回正确位置
arr[r], arr[store_index] = arr[store_index], arr[r]
return store_index
return select(0, len(arr) - 1, k)
# 示例
arr = [3, 2, 1, 5, 6, 4]
k = 2 # 寻找第2小的元素(索引从0开始)
result = quickselect(arr, k)
print(f"第{k+1}小的元素是: {result}") # 输出: 第3小的元素是: 3
时间复杂度分析
- 最好情况:每次划分都能将数组均匀分成两半,时间复杂度为O(n)
- 平均情况:通过随机化基准值,期望时间复杂度为O(n)
- 最坏情况:每次划分都极不均衡,时间复杂度为O(n²)
优化策略
- 随机化基准值:避免输入数据已排序时出现最坏情况
- 三数取中法:选择首、中、尾三个元素的中位数作为基准值
- 小数组使用插入排序:当子数组规模较小时,使用更简单的排序算法
应用场景
- 寻找数组的中位数
- 寻找前k小或前k大的元素
- 统计数据分析中的百分位数计算
快速选择算法通过巧妙的划分策略,在不需要完全排序的情况下快速定位目标元素,是处理顺序统计相关问题的有效工具。