第二章作业r
分治算法求第k小元素
一、算法描述
采用类似快速排序的划分思想进行分治。在数组中选取一个基准元素,把数组划分成两部分:左边元素都小于等于基准,右边都大于基准。划分完成后,基准就处在它在有序数组中的位置。
如果基准位置正好是第k小的位置,直接返回基准;
如果基准位置比目标位置大,说明第k小在左边区间,递归查找左区间;
如果基准位置比目标位置小,说明第k小在右边区间,递归在右区间找对应的第k小。
和快排不同,该算法只递归目标所在的一侧,不用处理两边。
二、伪代码
find_kth(arr, l, r, k):
if l == r:
return arr[l]
pos = partition(arr, l, r)
if pos == k-1:
return arr[pos]
elif pos > k-1:
return find_kth(arr, l, pos-1, k)
else:
return find_kth(arr, pos+1, r, k-pos-1)
partition(arr, l, r):
pivot = arr[l]
i = l - 1
for j from l to r:
if arr[j] <= pivot:
i = i + 1
swap arr[i], arr[j]
return i
三、时间复杂度分析
最好时间复杂度 O(n):每次划分都把数组大致均分。总计算量为 n + n/2 + n/4+...,总和是线性级别。
最坏时间复杂度 O(n^2):每次选的基准都是区间最大或最小元素,每次划分只能剔除一个元素,总计算量退化为 n+(n-1)+(n-2)+...。
算法平均期望时间复杂度为 O(n)。
四、对分治法的体会与思考
分治法核心是分、治、合:把大问题拆成结构相同、规模更小的子问题,递归求解子问题,再合并结果。
求第k小这个例子让我看到分治思想的灵活性和实用性。归并排序、快排需要处理全部子区间,但本题只需递归一侧,省去大量计算。
同时我也意识到,分治算法的效率往往取决于划分策略。基准选择不好,算法性能会大幅退化。分治不是固定代码模板,是一种问题拆解思路:遇到复杂问题,可以思考如何拆分,能否只求解部分子问题,以此降低时间开销。

浙公网安备 33010602011771号