分治算法思想

193 阅读2分钟

「这是我参与2022首次更文挑战的第22天,活动详情查看:2022首次更文挑战

“凡治众如治寡,分数是也” —— 《孙子兵法》

分治算法求解(分而治之的策略)

  • 分解:大规模问题(原问题) --> 分解 --> 小规模问题(子问题 —— 相同,只是规模较原问题小得多)
  • 治理:小规模问题之间是相互独立的,互不干扰
  • 合并:小规模问题的解(子问题) --> 合并 --> 大规模问题的解(原问题)

分治策略常用场景

合并排序

  • 分治算法的一个典型应用和完美体现
  • 平衡 + 简单的二分分治策略
  • 步骤
    • 分解:带排序元素 -> 分解 -> 两个子序列(大小大致相同)-> ... -> 分解至 -> 子序列中只有一个元素(单个元素的子序列是有序的)
    • 治理:子序列 -> 合并+排序(递归排序)
    • 合并:子序列 -> 合并 -> 有序序列
  • 异地排序(合并操作需要通过辅助合并函数完成)—— 先易后难
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
    • 递归占用的栈空间 === 递归树的深度(logn)

快速排序

  • 选取基准元素的方法
    • 第一个元素
    • 最后一个元素
    • 中间位置的元素
    • 第一个元素 + 最后一个元素 + 中间位置的元素 => 三者的中位数
    • 随机位置的元素
  • 步骤
    • 分解:小于/等于基准元素的子序列 + 基准元素 + 大于基准元素的子序列
    • 治理:对子序列递归快速排序
    • 合并:排序后的子序列进行合并 -> 原问题的解
  • 原地排序 —— 先难后易
  • 最好情况
    • (n/2)个元素 — + — 基准元素 — + — (n/2)个元素
    • 时间复杂度:O(nlgon)
    • 空间复杂度:O(logn)
  • 最坏情况
    • 划分:基准元素 — + — (n-1)个元素
    • 时间复杂度:O(n*n)
    • 空间复杂度:O(n)
  • 平均情况
    • 划分:(k-1)个元素 — + — 基准元素 — + — (n-k)个元素
    • 时间复杂度:O(nlogn)
    • 空间复杂度:O(nlogn)
  • 优化方案
    • 从右向左找小于基准元素的数 r[j]
    • 从左向右找大于基准元素的数 r[i]
    • 将找到的两个元素交换位置 -> 至 -> i === j
    • 基准元素于较大的元素交换位置
    int point(int r[], int min, int max) { // 优化划分方案
        int i = min, j = max, mid = r[min]; // 找基准元素
        while (i < j) {
            while(i < j && r[j] > mid) j--; // 从右向左
            while(i < j && r[i] < mid) i++; // 从左向右
            if (i < j) swap(r[i++], r[j--]); // 交换位置
        }
        if (r[i] > mid) {
            swap(r[i-1], r[min]); // 交换位置
            return i-1; // 返回新的基准值的位置
        }
        swap(r[i], r[min]); // 交换位置
        return i // 返回新基准元素的位置
    }