分治法

分治法的思想

将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

分治模式在每层递归时都有三个步骤:

  • 分解(Divide):将原问题分解为若干子问题,这些子问题是原问题的规模较小的实例。
  • 解决(Conquer):递归地求解这些子问题。当子问题规模足够小时,可直接求解。
  • 合并(Combine):合并子问题的解成原问题的解。

分析分治算法

当一个算法包含对自身的递归调用时,我们往往可以用递归式来描述其运行时间。按照分治模式的三个步骤依次分析。假设 T ( n ) T(n) T(n) 是问题规模为 n n n 的一个问题的运行时间。若问题规模足够小,如对于某个常量 c c c, n ≤ c n \le c n≤c,则直接求解需要常量的时间。假设把原问题分解为 a a a 个子问题,每个子问题的规模是原问题的 1 / b 1/b 1/b,则为了求解 a a a 个规模为 n / b n/b n/b 的子问题,我们需要 a T ( n / b ) aT(n/b) aT(n/b) 的时间。设分解的总时间为 D ( n ) D(n) D(n),合并的总时间为 C ( n ) C(n) C(n),则递归式如下:

注意:递归式可以有很多种形式。递归算法可能将问题划分为规模不等的子问题,子问题的规模也并不一定是原问题规模的固定比例(可能每次只比原问题少一个元素)。

分治法的经典例题

归并排序

一个经典的分治法的例子就是归并排序。

最大子数组问题

问题描述:
给定一个整数数组,找到一个具有最大和的连续子数组(最少包含一个元素),返回其最大和。

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

思路分析:
先来假设一下,如果一个数组只有一个值,那么它的最大子数组就是它自己。

nums = [n1]

如果一个数组有两个值呢?我们可以试着将它对半分,那么它的最大子数组必然存在于以下三种情况中:完全属于左半边、完全属于右半边或是跨越左右两边。具体来说,以 nums 为例,它的最大子数组必然是 [n1]、[n2] 或是 [n1, n2] 其中之一。

nums = [n1, n2]

现在考虑一下更一般的情况,假设这个数组有 n n n 个值,我们同样可以将它对半分。假设从中间 mid 分开,那么它的最大子数组必然属于以下三种情况之一:

  1. 完全属于左半边,即存在于子数组 nums[1..mid] 中。
  2. 完全属于右半边,即存在于子数组 nums[mid+1..n]
  3. 或是跨越中间值,一部分在左半边,一部分在右半边。
nums[1..n]

因此,我们的想法就是首先分解数组,然后解决子问题,判断最大子数组究竟是三种情况中的哪一种,最后合并子问题的答案以求得原问题的答案。

明显地,如果分解数组后最大子数组完全属于左半边或完全属于右半边,可直接用递归求解,因为这两个问题仍然是最大子数组问题,只是规模更小。我们先来考虑如何处理第三种情况,该问题并不是原问题的更小规模实例,因为它多了一个限制条件——子数组必须跨越中点。为了确保我们求出的最大子数组一定跨越中点,此处分开求左半边的最大和以及右半边的最大和,然后将他们相加。

// 求解跨越中点的最大子数组
int findMaxCrossingSubArray(vector<int> nums, int low, int mid, int high) {// check left partint left_sum = INT_MIN; // maximum sum of left partint sum = 0; // sum(nums[i..mid])for (int i=mid;i>=low;i--) {sum += nums[i];if (sum > left_sum) {left_sum = sum;}}// check right partint right_sum = INT_MIN; // maximum usn of right partsum = 0; // sum(nums[mid+1..i])for (int i=mid+1;i<=high;i++) {sum += nums[i];if (sum > right_sum) {right_sum = sum;}}return left_sum+right_sum;
}

解决了跨越中点的最大子数组问题后,我们就可以开始设计一个算法解决完整的最大子数组问题了。当数组的左右边界相等时,数组只有一个元素(这同时也是递归式的基本情形),它的最大子数组就是其本身,直接返回该元素即可。当数组有多个元素时,我们将其拆分并分情况讨论。

int findMaxSubArray(vector<int> nums, int low, int high) {if (low == high) {return nums[low];}int mid = (high-low)/2+low;int left_max = findMaxSubArray(nums, low, mid);int right_max = findMaxSubArray(nums, mid+1, high);int cross_max = findMaxCrossingSubArray(nums, low, mid, high);if (left_max>=right_max && left_max>=cross_max) {return left_max;} else if (right_max>=left_max && right_max>=cross_max) {return right_max;} else {return cross_max;}
}

矩阵乘法的 Strassen 算法

// TODO

代码实现

最大子数组问题

#include <iostream>
#include <vector>
#include <climits>
using namespace std;/*** @brief Find one of the maximum sum of crossing sub-array.* * @param nums * @param low * @param mid * @param high * @return int*/
int findMaxCrossingSubArray(vector<int> nums, int low, int mid, int high) {// check left partint left_sum = INT_MIN; // maximum sum of left partint sum = 0; // sum(nums[i..mid])for (int i=mid;i>=low;i--) {sum += nums[i];if (sum > left_sum) {left_sum = sum;}}// check right partint right_sum = INT_MIN; // maximum usn of right partsum = 0; // sum(nums[mid+1..i])for (int i=mid+1;i<=high;i++) {sum += nums[i];if (sum > right_sum) {right_sum = sum;}}return left_sum+right_sum;
}/*** @brief Find one of the maximum sum of sub-array.* * @param nums * @param low * @param high * @return int */
int findMaxSubArray(vector<int> nums, int low, int high) {if (low == high) {return nums[low];}int mid = (high-low)/2+low;int left_max = findMaxSubArray(nums, low, mid);int right_max = findMaxSubArray(nums, mid+1, high);int cross_max = findMaxCrossingSubArray(nums, low, mid, high);if (left_max>=right_max && left_max>=cross_max) {return left_max;} else if (right_max>=left_max && right_max>=cross_max) {return right_max;} else {return cross_max;}
}// test
int main(int argc, char const *argv[]) {vector<int> nums = {-2,1,-3,4,-1,2,1,-5,4};int res = findMaxSubArray(nums, 0, nums.size()-1);cout << res << endl;return 0;
}

算法与数据结构-分治法相关推荐

  1. c语言分治法求众数重数_算法实验二 分治法 众数问题.pdf

    算法实验二 分治法 众数问题 算法分析与设计实验二 分治法 主要内容 • 实验目的 • 主要实验仪器设备和环境 • 实验内容 • 实验要求 • 注意点 实验目的 • 理解分治法的基本思想 • 针对特定 ...

  2. 三大算法之一:分治法(带你用分治法思想优化程序,计算降低复杂算法的时间复杂度)

    目录 ​ 零.前言 1.分治法 1.含义 2.分治法主要思想 3.分治法的求解步骤 1.确定初始条件 2.计算每一部分的时间复杂度 3.合并时间复杂度 4.求解 3.最大最小值问题 1.问题描述 2. ...

  3. c++分治法求最大最小值实现_程序员:算法导论,分治法、归并排序,伪代码和Java实现...

    分治法 我们首先先介绍分治法.分治法的思想:将原问题分解为几个规模较小但类似于原问题的子问题,递归地求解这些子问题,然后在合并这些子问题的解来解决原问题的解. 还是拿扑克牌举例子,假设桌上有两堆牌面朝 ...

  4. aes算法实现c语言_以C语言实现归并排序为例,谈谈五大常用算法之一的“分治法”...

    分治算法,顾名思义就是"分而治之",即把规模较大的复杂问题拆分为若干规模较小的类似子问题,并逐个解决,最后再将各个子问题的解决结果合并,得到原始问题的结果的方法.这个技巧是很多高效 ...

  5. 用递归与分治策略求解网球循环赛日程表_算法设计:分治法(比赛日程安排)...

    一.算法思路 1.思路 分治算法的思想是:对于一个规模位N的问题,若该问题可以容易解决(比如规模N较小),则直接解决,否则将其分解为M个规模较小的子问题,这些子问题互相独立,并且与原问题形式相同,递归 ...

  6. Java常用算法二:分治法

    文章目录 一.分治算法的基本步骤 二.分治算法解决汉诺塔问题 2.1 汉诺塔的规则: 2.2 使用分治算法 笔记参考:尚硅谷 分治法就是把很复杂的问题分而治之,把一个很大的问题分成几个很小的问题,再把 ...

  7. 算法设计——用分治法查找数组元素的最大值和最小值、用分治法实现合并排序、最小费用问题、树的最大连通分支问题(代码实现)

    代码链接:pan.baidu.com/s/15inIth8Vl89R1CgQ_wYc2g  提取码:gf13 算法分析与设计第 1 次实验 时间 2020.3.31 地点 软件大楼 127 实验名称 ...

  8. 五大常用算法之四:分治法

    分治法和动态规划有点像,都是分解成子问题 中科大的张署老师课件很清楚,摘录如下: 1.什么是分治法 当求解的问题较复杂或规模较大时,不能立刻得到原问题的解,但这些问题本身具有这样的特点,它可以分解为若 ...

  9. 0008算法笔记——【分治法】循环赛事日程表

    问题描述: 设有n=2^k个运动员要进行网球循环赛.现要设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次:      (2)每个选手一天只能参赛一次:      (3) ...

最新文章

  1. java mvc 绑定_关于Java:Spring MVC:将请求属性绑定到控制器方法参数
  2. 好久没有看到这么有建设性德文章,由衷地赞叹《知其所以然地学习(以算法学习为例)》-By 刘未鹏(pongba)
  3. Factory模式与Prototype模式的异同
  4. Kubernetes — Project Layout 与 cmd 入口设计
  5. 干货 | 十年经验PM手机里都装了哪些app?
  6. QT5.11 + VS2017 环境搭建
  7. Netty--ByteBuf
  8. 华中地区高校第七届ACM程序设计大赛——之字形矩阵【2012年5月27日】
  9. IOS 应用中从竖屏模式强制转换为横屏模式
  10. 9.广义霍夫变换——广义霍夫变换算法和识别中的应用、现在的霍夫算法及识别中的应用_2
  11. apt-get update命令卡在waiting for headers
  12. ORACLE rollup函数
  13. 一种简单的输出负电源电路(部分电平转换芯片的工作原理)
  14. php调京东联盟接口,使用京东联盟API获取自定义促销链接
  15. 大数据思维的核心是什么?
  16. Win10桌面图标排列混乱
  17. 把ipa文件上传到App Store教程步骤
  18. 抖音神曲《一百万个可能》:“在一瞬间,我们有一百万个可能”
  19. 电子公文技术获得突破 方正CEB渐成标准
  20. 探索未来|一文看懂小米年度技术峰会·硬件专场

热门文章

  1. 可汗学院金融学笔记系列
  2. 谷歌浏览器解决跨域问题 --disable-web-security --user-data-dir
  3. singletask和onNewintent
  4. Java 基础 面试题
  5. 为什么必须要数据清理?
  6. 作业5 | AR模型参数的估计
  7. 实例演绎Unix/Linux的一切皆文件思想
  8. 什么是多态?为什么使用多态?
  9. C语言编写Johnson-Trotter算法生成排列
  10. Coursera | Introduction to Data Science in Python(University of Michigan)| Assignment2