第六章 递归与分治法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
二分搜索技术
给定已按升序排好序的n个元素a[0:n-1],现要在这n个元素中找 出一特定元素x。 算法复杂度分析: 据此容易设计出二分搜索算法: 每执行一次算法的 public static int binarySearch(int [] a, int x, int n) while循环, 待搜索数 { 组的大小减少一半。因 // 在 a[0] <= a[1] <= ... <= a[n-1] 中搜索 x // 找到x时返回其在数组中的位置,否则返回-1 此,在最坏情况下, int left = 0; int right = n - 1; while循环被执行了 while (left <= right) { O(logn) 次。循环体内 int middle = (left + right)/2; 运算需要O(1) 时间, if (x == a[middle]) return middle; if (x > a[middle]) left = middle + 1; 因此整个算法在最坏情 else right = middle - 1; 况下的计算时间复杂性 } 为O(logn) 。 return -1; // 未找到x
回归
递归
优点
结构清晰、可读性强、容易证明算法的正确性
缺点
运行效率低
理论上,所有的递归算法都可以转换成非递归算 法
通过分析,跳过分解部分,直接用循环结构实现求值 过程。 用栈保存程序的运行过程。 利用栈保存参数。
Fibonacci数列非递归:
int Fibo(int n) { if(n==1||n==2)return 1 ; int a1=1,a2=1; int an ; for(int i=3;i<n;i++) { an=a1+a2; a1=a2; a2=an; } return an; }
这条特征涉及到分治法的效率,如果各子问题是不独 立的,则分治法要做许多不必要的工作,重复地解公 共的子问题,此时虽然也可用分治法,但一般用动态 规划较好。
分治法的求解过程
一般来说,分治法的求解过程由以下三个阶段组成:
(1)划分:既然是分治,当然需要把规模为n的原问题划分为k个规模较小的 子问题,并尽量使这k个子问题的规模大致相同。
递归方程 边界条件与递归方程是递归函数的二个要素,递归函 数只有具备了这两个要素,才能在有限次计算后得出 结果。
6.1
递归
例 Fibonacci数列
无穷数列1,1,2,3,5,8,13,21,34,55,…,被 称为Fibonacci数列。它可以递归地定义为:
1 n0 F ( n) 1 n 1 F (n 1) F (n 2) n 1
6.2 递归法应用
汉若塔问题
递归法的时间复杂度为O(2n) 递归法的时间复杂度为O(2n)
Fibonacci数列问题
八皇后问题
递归法的时间复杂度为O( nn )
Hanoi塔问题
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘, 这些圆盘自下而上,由大到小地叠在一起。各圆盘从小 到大编号为1,2,…,n,现要求将塔座a上的这一叠圆盘移到 塔座b上,并仍按同样顺序叠置。在移动圆盘时应遵守以 下移动规则:
⑴ 边界条件:确定递归到何时终止; ⑵ 递归方程:大问题是如何分解为小问题的。
算法总体思想
对这k个子问题分别求解。如果子问题的规模仍然不 将要求解的较大规模的问题分割成k个更小规模的子 够小,则再划分为k个子问题,如此递归的进行下去, 问题。 直到问题规模足够小,很容易求出其解为止。
T(n)
=
n
(2)求解子问题:各子问题的解法与原问题的解法通常是相同的,可以用递 归的方法求解各个子问题,有时递归处理也可以用循环来实现。
(3)合并:把各个子问题的解合并起来,合并的代价因情况不同有很大差 异,分治算法的有效性很大程度上依赖于合并的实现。
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,...,yk); //将各子问题的解合并为原问题的解 }
}
思考题:给定a,用二分法设计出求an的算法。
例:计算an,应用分治技术得到如下计算方法:
a
n
n a
a 2 n ´a
34
2
如果 n 1 如果 n 1
Baidu Nhomakorabea
32 31 31 3 9
32
分解问题 31 求解每个子问题 3
31
3 9
3
合并子问题的解
81
不是所有的分治法都比简单的蛮力法更有效。
求解规模为n的问题可以转化为一个或多个结 构相同、规模较小的问题,然后从这些小问 题的解能方便的构造出大问题的解。 递归调用的次数必须是有限的。 必须有结束递归的条件(边界条件)来终止 递归。即当规模n=1时,能直接得解。
6.1.2 递归的执行过程
递推
把规模为n的问题的求解推到比原问题的规模 较小的问题求解,且必须要有终止递归的情 况。 当获得最简单情况的解后,逐级返回,依次 得到规模较大问题的解。
=
n/2
n
n/2 n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
算法总体思想
将求出的小规模的问题的解合并为一个更大规模的问 题的解,自底向上逐步求出原来问题的解。
第n个Fibonacci数可递归地计算如下: public static int fibonacci(int n) { if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(n-2); }
边界条件
递归方程
6.1.1 递归算法的特性
三个特性
第六章 递归与分治法
递归
汉诺塔、斐波那契数列、八皇后问题
分治
二分法求方程近似解
6.1
递归
递归是一种在函数和方法中调用自身的编 程技术。 递归(Recursion)就是子程序(或函数) 直接调用自己或通过一系列调用语句间接 调用自己,是一种描述问题和解决问题的 基本方法。 递归有两个基本要素:
规则1:每次只能移动1个圆盘; 规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上; 规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任 一塔座上。
n后问题
在n×n格的棋盘上放置彼此不受攻击的n个皇后。按照国际象 棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线 上的棋子。n后问题等价于在n×n格的棋盘上放置n个皇后, 任何2个皇后不放在同一行或同一列或同一斜线上。
T(n/2)
T(n/2)
T(n/2)
T(n/2)
算法总体思想
将求出的小规模的问题的解合并为一个更大规模的问 对这k个子问题分别求解。如果子问题的规模仍然不 题的解,自底向上逐步求出原来问题的解。 够小,则再划分为k个子问题,如此递归的进行下去, 直到问题规模足够小,很容易求出其解为止。
T(n)
n/2
穷举法
在可能的解空间逐一尝试(8次比较) 分组(4次比较)
分治法
6.3.1 分治法概述
分治法所能解决的问题一般具有以下几个特征: 该问题的规模缩小到一定的程度就可以容易地解 决; 该问题可以分解为若干个规模较小的同构子问题, 即该问题具有最优子结构性质。 利用该问题分解出的子问题的解可以合并为该问 题的解; 该问题所分解出的各个子问题是相互独立的,即 子问题之间不包含公共的子问题。
1 Q 2 Q 3 Q 4 Q 5 Q 6 Q 7 Q 8 Q 1 2 3 4 5 6 7 8
6.3 分治法
分治法的设计思想是,将一个难以直接解 决的大问题,分割成一些规模较小的相同 问题,以便各个击破,分而治之。
凡治众如治寡,分数是也。
----孙子兵法
6.3.1 问题提出
16枚硬币,一枚伪造硬币,其重量要比真 硬币轻。任务:找出伪造硬币。
T(n)
n/2
=
n/2
n
n/2 n/2
T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4) T(n/4)T(n/4)T(n/4)T(n/4
6.1
例
递归
边界条件
阶乘函数
阶乘函数可递归地定义为:
n0 1 n! n(n 1)! n 0
6.4 分治法应用
快速排序、归并排序、二分查找 二分法求方程近似解
算法分析
将方程的有解空间平分为两个小区间,然后判断 解在哪个小区间; 继续把有解空间一分为二进行判断,如此周而复 始,直到求出满足精确要求的近似解。
步骤
二分搜索技术
给定已按升序排好序的n个元素a[0:n-1],现要在这n个元素中找 出一特定元素x。 分析: 该问题的规模缩小到一定的程度就可以容易地解决; 该问题可以分解为若干个规模较小的相同问题; 分解出的子问题的解可以合并为原问题的解; 分解出的各个子问题是相互独立的。 分析:如果n=1即只有一个元素,则只要比较这个元素和x就 分析:比较x和a的中间元素a[mid],若x=a[mid],则x在L中的 可以确定x是否在表中。因此这个问题满足分治法的第一个适 位置就是mid;如果x<a[mid],由于a是递增排序的,因此假 分析:很显然此问题分解出的子问题相互独立,即在a[i]的前 用条件 如x在a中的话,x必然排在a[mid]的前面,所以我们只要在 面或后面查找x是独立的子问题,因此满足分治法的第四个适 a[mid]的前面查找x即可;如果x>a[i],同理我们只要在a[mid] 用条件。 的后面查找x即可。无论是在前面还是后面查找x,其方法都 和在a中查找x一样,只不过是查找的规模缩小了。这就说明 了此问题满足分治法的第二个和第三个适用条件。
启发式规则:
1. 平衡子问题:最好使子问题的规模大致相同。也就是将一 个问题划分成大小相等的k个子问题(通常k=2),这种使子 问题规模大致相等的做法是出自一种平衡(Balancing)子问 题的思想,它几乎总是比子问题规模不等的做法要好。 2. 独立子问题:各子问题之间相互独立,这涉及到分治法的 效率,如果各子问题不是独立的,则分治法需要重复地解公 共的子问题。