算法第二章实践作业

  1. 找第 k 小的数的分治算法描述
    自然语言描述:
    在当前区间 a[left...right] 中选择一个基准元素(比如选 a[left])。
    调用 partition 函数,以基准元素将区间划分为两部分:左段所有元素 ≤ 基准,右段所有元素 ≥ 基准,基准元素最终落在位置 p。
    计算基准元素是当前区间的第几个数:rank = p - left + 1。
    比较 rank 和 k:
    如果 rank == k,说明 a[p] 就是第 k 小的数,直接返回。
    如果 rank > k,说明第 k 小的数在左段,递归在 a[left...p-1] 中找第 k 小。
    如果 rank < k,说明第 k 小的数在右段,递归在 a[p+1...right] 中找第 k - rank 小。
  2. 时间复杂度分析
    最好情况:O(n)
    每次划分都能将区间大致平分为两半,递归式为 T(n) = T(n/2) + O(n)。根据主定理,T(n) = O(n)。也就是说每次只递归一边,问题规模减半,总代价是 n + n/2 + n/4 + … = 2n = O(n)。
    最坏情况:O(n²)
    每次划分都极不平衡,基准元素是当前区间的最大值或最小值,导致一边为空,另一边只减少一个元素。递归式为 T(n) = T(n-1) + O(n),展开后为 n + (n-1) + (n-2) + … + 1 = O(n²)。这种情况发生在数组已有序(或逆序)且固定选首元素为基准时。
    平均情况:O(n)
    随机情况下,划分点大致均匀分布,期望递归深度为对数级,总期望代价为 O(n)。
  3. 对分治法的体会和思考
    第一,分治的核心是"分而治之"。 把一个大问题拆成若干个结构相同的小问题,递归求解后再合并结果。找第 k 小的数、归并排序、最大子段和,本质上都是这个思路。
    第二,划分的质量直接决定效率。 找第 k 小的数之所以最坏会退化到 O(n²),就是因为划分可能极度不平衡。而归并排序每次固定对半切,所以能稳定保持 O(n log n)。这让我意识到,分治算法的设计关键在于如何"分"——分得越均衡,效率越高。
    第三,分治不一定需要合并。 找第 k 小的数只需要递归其中一边,不需要合并;而归并排序和最大子段和都需要合并步骤。这说明分治有三种形态:只递归一边(如快速选择)、两边都递归但不合并(如某些遍历)、两边都递归且需要合并(如归并排序)。
    第四,分治和递归密不可分。 写分治算法时一定要先想清楚终止条件是什么,否则容易无限递归。比如找第 k 小的数,终止条件是区间只剩一个元素;归并排序的终止条件是区间长度为 1。
posted @ 2026-10-12 21:33  yang31  阅读(6)  评论(0)    收藏  举报