研究生课程 算法分析-分治法
2016-12-29 19:46
316 查看
分治法(divide and conquer)是算法分析里比较直观和朴素的思想,应用也很广泛。
可以用下面的伪代码来描述,
一般有下面几种解决的办法,
用自定义栈,做了编译器做的事,优化效果不明显。
用递推的方法来实现递归函数
通过变换,将一些递归转化成尾递归(尾递归是所有子递归出现在递归函数的尾部),迭代求结果
后两种方法虽然范围有限,但是如果可以在问题中这样优化的话,效果会比较好。
小规模时,问题容易求解,这个一般都很容易满足。
问题具有 最优子结构 的性质,即可以分解成若干个规模较小的相同问题。
子问题的解可以 合并为该问题的解
子问题相互独立,即 不包含公共的子问题
满足前三条特征,可以用分治法;如果不具备第三条特征,可以考虑贪心算法和动态规划。如果问题满足第 4 个特征的话,分治法会重复计算导致效率,用动态规划较好。
有递归和非递归两种实现方法。
注意快排的划分函数
分治法的思想
分治法的思想是,把一个复杂的问题 P 划分称 k 个子问题,这些子问题相互独立且与原问题相同。递归调用子问题,直到问题规模足够小,可以很容易地求解为止;接着,把小规模的问题的解合并成一个更大规模的问题的解。可以用下面的伪代码来描述,
divide-and-conquer(P) { if (|P| <= n0) adhoc(P); // 小规模问题求解 divide P into smaller subinstances P1, P2,...,Pk; // 分解问题成小规模问题 for (i = 1; i <= k; ++ i) yi = divide-and-conquer(Pi); // 递归解决子问题 return merge(y1, y2,...,yk); // 合并为原问题的解 }
分治法和递归
从分治法的思想可以看出,在求解子问题的时候往往需要用到递归,因此分治法和递归经常一起出现。递归算法的优点是结构清晰,可读性强,一般代码也比较容易写,但是缺点是运行效率很低,无论是计算时间还是占用的存储空间都很大。一般有下面几种解决的办法,
用自定义栈,做了编译器做的事,优化效果不明显。
用递推的方法来实现递归函数
通过变换,将一些递归转化成尾递归(尾递归是所有子递归出现在递归函数的尾部),迭代求结果
后两种方法虽然范围有限,但是如果可以在问题中这样优化的话,效果会比较好。
分治法的适用条件
看一个问题是否可以用分治法求解,可以考虑这个问题是否符合下面几种条件,小规模时,问题容易求解,这个一般都很容易满足。
问题具有 最优子结构 的性质,即可以分解成若干个规模较小的相同问题。
子问题的解可以 合并为该问题的解
子问题相互独立,即 不包含公共的子问题
满足前三条特征,可以用分治法;如果不具备第三条特征,可以考虑贪心算法和动态规划。如果问题满足第 4 个特征的话,分治法会重复计算导致效率,用动态规划较好。
分治法的应用
二分查找算法
二分查找要解决的问题是,从有序数组 a 中找出特定元素 x 出来。因为是有序,所以可以用考虑分治法,从线性扫描的复杂度 O(n) 优化到 O(logn)。有递归和非递归两种实现方法。
大整数的乘法
大整数的乘法,分治成较小规模的子问题后,从四次乘法的优化到三次乘法,可以把复杂度从 O(n2) 优化到 O(nlog3=1.59),有较大的改进。Strassen 矩阵乘法
与大数乘法的例子类似,做分治后想办法减少乘法的次数,从 O(n3) 优化到了 O(nlog7=2.81) 的复杂度。归并排序
基本思想是,把待排序的数组分成两个子集,分别排序后再合并。递归代码如下,void merge_sort(int a[], int start, int end) { if (start < end) { int mid = (start + end) / 2; merge_sort(a, start, mid); // 对左边的序列递归排序 merge_sort(a, mid + 1, end); // 对右边的序列递归排序 merge(a, start, end); // 合并子序列 } }
快速排序
快排的想法很简单,是想找一个数作为排序的基准(pivot),小的放左边,大的放右边。然后对左边和右边的两个子问题递归排序。递归实现如下,void quick_sort(int a[], int start, int end) { if (start < end) { // int pivot_index = partition(a, start, end); // 挖坑划分 quick_sort(a, start, pivot_index - 1); // 左边的子序列递归排序 quick_sort(a, pivot_index + 1, end); // 右边的子序列递归排序 } }
注意快排的划分函数
partition,用的是挖坑填坑的方法。以第一个元素为基准(pivot),最后返回 pivot 的位置 pivot_index。此时左边的数组元素都比 pivot 小,右边的数组元素都比 pivot 大。
相关文章推荐
- 研究生课程 算法分析-分治法
- 研究生课程 算法分析-动态规划
- 研究生课程 算法分析-回溯法和分支限界法
- 研究生课程 算法分析-贪心法
- 研究生课程 算法分析-回溯法和分支限界法
- 研究生课程 算法分析-贪心法
- 研究生课程 算法分析笔记
- 研究生课程 算法分析-动态规划
- 研究生课程 算法分析笔记
- 算法分析--分治法
- 算法分析之分治法总结(二)
- 计算机算法设计与分析作业01:分治法求解大数乘法+L型骨牌的棋盘覆盖问题
- 美国计算机科学与工程专业本科和硕士研究生课程设置特点分析——基于22所美国著名大学统计数据的分析
- 算法设计与分析学习-分治法1
- 算法分析课程笔记(二)
- 算法分析与设计课程(3):【leetcode】Permutations
- --算法分析与设计--课程作业--【顺序统计】--【采用链表法散列表】--【开放地址法(双重散列)】
- 算法中的递归分析和分治法的原理
- 算法分析与设计课程(1):Add Two Numbers
- 研究生一年级上学期课程分析