第2章-递归与分治策略-习题与实验共38页
合集下载
02计算机算法-第二章-递归-2011

2012-10-20
3. 子程序调用的内部操作——两个
执行调用时,系统需先完成三件事:(保护现场) (1)返回地址进栈,为被调用算法的局部变量分配存储空间; (2)为被调用算法准备数据:计算实参,并赋给对应的栈顶形参; (3)将控制转移到被调用算法的入口。 从被调用算法返回调用算法时,系统要完成的事: (恢复现场)
2.3 递归转成非递归
• 递归的优点:与数学定义相似,结构清晰,可读性强,易 证明,算法设计方便。 • 递归的缺点:运行效率较低,耗时,空间占用多,重复计 算量多。 • 消去递归: – 目的是克服递归时间、空间的开销
– 解决方法:先使用递归,然后证明所设计的递归算法正 确并且确信是一个好算法,再把递归消去,翻译成与之 等价的只使用迭代的算法。 – 直接递归翻译成迭代过程的规则
2012-10-20
#include "stdio.h" #define n 7 void split (int a[], int t) { int k, i, j, L;
for(k=1;k<=t;k++) printf("%d , ",a[k]); printf("\n");
/*t是要拆分的数*/
1 4 2 4 1 2 3 1 2 1 3 2
4 1 4 2 2 1 1 3 1 2 2 3
对于n个元素 a=(a1a2……ak……an), 设过程range(a, k, n)是求a的第k 到第n个元素的全排列。 算法如下:
procedure range(a,k,n) if k=n then print(a) else for i ← k to n do { a[k] a[i]; procedure range(a,k,n); call range(a,k+1,n); 当k指向最后元素时, a[k] a[i]; 递归终止,输出相应的字符串a } 否则 endif endrange; i从k到n重复执行: 交换ak与ai ; range(a, k+1, n); 交换ak与ai ; endrange; 2012-10-20
算法第2章分治的策略

满足 Fn=Fn-1+Fn-2
增加F0=0,得到数列 0,1, 1, 2, 3, 5, 8, 13, 21, …
通常算法:从 F0, F1, …, 根据定义陆续相加,时间为Q(n)
定理2.1 设 {Fn}为 Fibonacci 数构成的数列,那么
Fn1
Fn
Fn Fn1
1 1
1n 0
算法:令矩阵
M
2i + 2j + 2k = n 2i+j > 2k+j i > k 注:当n是奇数时,用其他芯片测试轮空芯片,如果轮空 芯片是好的,算法结束;否则淘汰轮空芯片.
每轮淘汰后,芯片数至少减半,时间复杂度是:
W (n)W (n)O (n)
2
n3
W (n)O (n)
W (n)1
n3
8
伪码描述
算法2.3 Test(n) 1.k n 2.while k >3 do 3. 将芯片分成 k/2 组 // 如有轮空芯片,特殊处理 4. for i =1 to k/2 do
10
T(1n0)
n
T(1) 0
T(n) Θ(nlogn)
24
均衡划分
n
n
9n
10
10
n
9n 9n
81 n
100
100 100
100
81 n
729 n
1
ቤተ መጻሕፍቲ ባይዱ1000
1000
O(n) O(n) O(n)
O(n)
k l og10 n
9
… ……
1 O(nlogn)
25
平均情况下时间复杂度
假设输入数组首元素排好序后的正确位置处在1,2,…,n 各种 情况是等可能的,概率为1/n.
算法设计与分析耿国华第二章

Chapter
Hale Waihona Puke 22.4.1替换方法
例2.6 汉诺塔算法(见例2.5)的时间复杂 度分析。 假设汉诺塔算法的时间复杂度为T(n),例 2.5的算法递归方程为:
n 1 1 T ( n) 2T (n 1) 1 n 1
Chapter
2
2.4.1替换方法
利用替换法求解该方程:
T (n) 2T (n 1) 1
2
2.2具有递归特性的问题
• (2)递归定义的数据结构
在数据结构中,如广义表、二叉树、树等结构 其本身均具有固有的递归特性,可以自然地采 用递归法进行处理。 (3)递归求解方法 许多问题的求解过程可以用递归分解的方法描 述,例如:计算两非负整数最大公约数的欧几 里得算法和著名的汉诺塔问题(Hanoi)问题。
T (n) O(2n )
得到该算法的时间复杂度
Chapter
2
2.4.1替换方法
例2.7 2-路归并排序的递归算法分析。 假设初始序列含有n个记录,首先将这n个记 录看成n个有序的子序列,每个子序列的长度为1, 然后两两归并,得到 n / 2个长度为2(n为奇数 时,最后一个序列的长度为1)的有序子序列; 在此基础上,再对长度为2的有序子序列进行两 两归并,得到若干个长度为4的有序子序列;如 此重复,直至得到一个长度为n的有序序列为止。 这种方法被称作2-路归并排序。
F(6) F(5) F(4) F(3) F(2) F(1) F(0) F(1) F(1) F(2) F(3) F(2) F(1) F(2) F(1) F(0) F(3) F(1) F(1) F(4) F(2) F(0)
F(0) F(1) F(0)
图2-1 斐波那契算法的递归结构(n=6)
第二章 递归

§2 递归算法的应用和实现
如果问题的数据结构是递归的,问题的定义是递归的,问 题的解法是递归的,可以考虑用递归算法。 1)问题的数据结构是递归的 看下面的数据结构:
Head
Head
^ … ^
这是单链表,每个结点有两个域:数据域和指针域。它是 一种递归的结构,可定义为: 1)一个结点,其指针域为null,是一个单链表; 2)一个结点,其指针域为有效指针指向一个单链表,仍是一个 单链表。
North China Electric Power University
Fib n:3 f1:1 f2:1 f: 2 Fib n: 2 f1:1 f2:0 f: 1 Fib n: 1 f1: f2: f: 1
Fib n: 1 f1: f2: f: 1
Fib n: 0 f1: f2: f: 0
North China Electric Power University
第二章 递归 (Recursion)
★ ★
递归的概念 递归算法的应用和实现
★ 递归问题的非递归算法
★
★
递归方程渐进阶的求法
生成函数
North China Electric Power University
§1 递归的概念
一个直接或间接调用自身的算法称为递归算法。 一个使用函数自身给出定义的函数称为递归函数。
3)问题的解法是递归的 例:整数划分问题 将一个正整数n表示成一系列正整数之和, n=n1+n2+…+nk,其中n1≥n2≥…≥nk≥ 1,k ≥1 正整数n的一个这种表示称为正整数n的一个划分。正整数n的 不同划分个数称为正整数n的划分数,记做p(n)。 如正整数6有如下11种不同的划分,所以p(6)=11。 6; 5+1; 4+2,4+1+1; 3+3,3+2+1,3+1+1+1; 2+2+2,2+2+1+1,2+1+1+1+1, 1+1+1+1+1+1;
递归和分治策略

二、实验内容、过程和结果(实验主要内容的介绍、主要的操作步骤、程序代码和测试数据及实验结果)
1.内容介绍:
分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。下面将用分治思想设计出求一组数据中的最大值与最小值方案
2.程序代码:
#include<iostream>
成绩
评阅老师
using namespace std;
int max(int a,int b);
int min(int a,int b);
void max_min(int *num,int l,int r,int &maxnum,int &minnum)
{
if(l==r) //数组只有一个元素
{
maxnum=num[l];
实验报告
《算法设计与分析》课程
《算法设计与分析》课程实验报告(一)
实验题目
用递归和分治策略求解数组中最大值与最小值
实验时间
一、实验目的及要求
1.理解递归的概念
2.掌握设计有效算法的分治策略
3.通过二分搜索技术,大整数乘法,Strassen矩阵乘法,棋盘覆盖,合并排序和快速排序,线性时间选择,
最接近点对问题,循环赛日程表学习分治策略设计技巧
}
int m =(l+r)/2;
int lmax,lmin;
max_min(num,l,m,lmax,lmin); //递归求左半部分最大最小值
int rmax,rmin;
max_min(num,m,r,rmax,rmin); //递归求右半部分最大最小值
maxnum = max(lmax,rmax);
1.内容介绍:
分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。下面将用分治思想设计出求一组数据中的最大值与最小值方案
2.程序代码:
#include<iostream>
成绩
评阅老师
using namespace std;
int max(int a,int b);
int min(int a,int b);
void max_min(int *num,int l,int r,int &maxnum,int &minnum)
{
if(l==r) //数组只有一个元素
{
maxnum=num[l];
实验报告
《算法设计与分析》课程
《算法设计与分析》课程实验报告(一)
实验题目
用递归和分治策略求解数组中最大值与最小值
实验时间
一、实验目的及要求
1.理解递归的概念
2.掌握设计有效算法的分治策略
3.通过二分搜索技术,大整数乘法,Strassen矩阵乘法,棋盘覆盖,合并排序和快速排序,线性时间选择,
最接近点对问题,循环赛日程表学习分治策略设计技巧
}
int m =(l+r)/2;
int lmax,lmin;
max_min(num,l,m,lmax,lmin); //递归求左半部分最大最小值
int rmax,rmin;
max_min(num,m,r,rmax,rmin); //递归求右半部分最大最小值
maxnum = max(lmax,rmax);
第二章 递归

图2.3 计算fib(4)的递归调用树
由于递归引起一系列的函数调用,并且可能会 有一系列的重复计算,递归算法的执行效率相 对较低。如图2.2所示,Fib(2)重复计算2次, Fib(1)重复计算3次等。 所以,当某个递归算法能方便地转换成递推算 法时,通常就按递推算法编写程序。例如上例 计算斐波那契数列的第n项的函数fib(n)应采 用递推算法,即从斐波那契数列的前两项值出 发,逐次由前两项计算出下一项,直至计算出 要求的第n项。
void comb(int m, int k, int r) { int i, j; for(i = m; i >= k; i--) { a[k-1] = i; if (k > 1) /* 组合中还有其它元素 */ comb(i-1, k-1, r); else { /* 找到了一个组合,将组合的元素输出 */ for(j = r-1; j >= 0; j--) printf("%4d", a[j]); printf("\n"); void main() { } int r, m; printf(“enter m & r: “); } scanf(“%d%d”, &m, &r); comb(m, r, r); }
当算法调用链上的某两个算法为同一个算法时, 称这种算法实现方式为递归的。通过递归方式 完成其功能的算法称为递归算法。许多问题的 求解方法具有递归特征,用递归算法描述这种 求解算法,能使算法更简洁。计算n的阶乘(n!) 算法就是一个很好的例子。因 n! = 1 * 2 * 3 * … * n
【例2.1】用循环实现阶乘计算 long fac(int n) { long s; int i; for(s = 1L, i = 1; i <= n; i++) s *= i; return s; }
第2章(397)教材配套课件

2
then return n
3 return F(n 1) + F(n 2)
第2章 分治法
图2-1表示斐波那契算法在输入规模n=6时的递归结构, 其中的每个F(·)表示对递归函数的一次调用,叶结点表示递 归终止时的调用,即斐波那契算法F(n)中第2行的返回值。
第2章 分治法 图2-1 斐波那契算法的递归结构(n=6)
主方法依赖于以下定理。这个定理也称为主定理。
第2章 分治法 定理2.1 设a≥1,b>1为常数,f(n)为一函数。T(n)由 以下递归方程定义:
T(n)=aT(n/b)+f(n) 其中n为非负整数,则T(n)有如下的渐近界限:
(1) 若对某些 常数 > 0,有 f(n) = O(nlogb a-),那么 T(n) =
只要选取d≥(16/13)c,最后一步便成立。
第2章 分治法
2.1.4 主方法 主方法(master method)为我们提供了解如下形式递归方
T(n)=aT(n/b)+f(n)
(2.1)
其中a≥1,b>1为常数,f(n)是渐近正函数。递归方程(2.1)描
述了算法的运行时间。算法将规模为n的问题划分成a个子问
递归过程在实现时,可用一个等价的递归栈来实现过程 的嵌套调用。递归的深度就是在整个计算中过程嵌套调用的 最大程度。通常,深度取决于输入规模。因此,对于大型问 题,栈所需的空间可能妨碍我们使用递归方法求解。图2-3 表示n=4时汉诺塔算法的运行过程。
第2章 分治法 图 2-3 汉诺塔的运行过程(n=4)
( nlogb a)。
(2) 若 f(n) = (nlogb a),那么 T(n) = ( nlogb a lg n)。
4-递归与分治策略

1# L
左岸
3
2#
R
右岸
2
当 y = 2 时, Jump ( 0 , 2 ) = 3 ; 说明:河中有两片荷叶时,可以过 3 只青蛙。起始时: 1#,2#,3# 3只青蛙落在 L 上, 第一步:1# 从 L 跳至叶 1上, 第二步:2# 从 L 跳至叶 2上, 第三步:3# 从 L 直接跳至 R 上, 5 1 第四步:2# 从叶 2 跳至 R 上, 叶1 第五步:1# 从叶 1 跳至 R 上,
2.1 递归的概念
例5 整数划分问题 前面的几个例子中,问题本身都具有比较明显的递归关系,因 而容易用递归函数直接求解。 在本例中,如果设p(n)为正整数n的划分数,则难以找到递归关 系,因此考虑增加一个自变量:将最大加数n1不大于m的划分个 数记作q(n,m)。可以建立q(n,m)的如下递归关系。
3. 先看简单情况,河中无柱子:S = 0 , Jump ( 0 , y ) 2 ; 当 y = 1 时,Jump ( 0 , 1 ) = 说明:河中有一片荷叶,可以过两只青蛙,起始时 L 上有 两只青蛙,1# 在 2# 上面。 第一步:1# 跳到荷叶上; 第二步:2# 从 L 直接跳至 R 上; 第三步:1# 再从荷叶跳至 R 上。 如下图: 1
递归小结
优点:结构清晰,可读性强,而且容易用数学归纳法来证明算 法的正确性,因此它为设计算法、调试程序带来很大方便。 缺点:递归算法的运行效率较低,无论是耗费的计算时间还是 占用的存储空间都比非递归算法要多。
案例:青蛙过河
一条小溪尺寸不大,青蛙可以从左岸跳到右岸,在左岸有 一石柱L,面积只容得下一只青蛙落脚,同样右岸也有一石柱R, 面积也只容得下一只青蛙落脚。有一队青蛙从尺寸上一个比一个 小。我们将青蛙从小到大,用1,2,…,n编号。规定初始时这 队青蛙只能趴在左岸的石头L上,按编号一个落一个,小的落在 大的上面。不允许大的在小的上面。在小溪中有S根石柱,有y 片荷叶,规定溪中的柱子上允许一只青蛙落脚,如有多只同样要 求按编号一个落一个,大的在下,小的在上,而且必须编号相邻。 对于荷叶只允许一只青蛙落脚,不允许多只在其上。对于右岸的 石柱R,与左岸的石柱L一样允许多个青蛙落脚,但须一个落一 个,小的在上,大的在下,且编号相邻。当青蛙从左岸的L上跳 走后就不允许再跳回来;同样,从左岸L上跳至右岸R,或从溪 中荷叶或溪中石柱跳至右岸R上的青蛙也不允许再离开。问在已 知溪中有S根石柱和y片荷叶的情况下,最多能跳过多少只青蛙?
左岸
3
2#
R
右岸
2
当 y = 2 时, Jump ( 0 , 2 ) = 3 ; 说明:河中有两片荷叶时,可以过 3 只青蛙。起始时: 1#,2#,3# 3只青蛙落在 L 上, 第一步:1# 从 L 跳至叶 1上, 第二步:2# 从 L 跳至叶 2上, 第三步:3# 从 L 直接跳至 R 上, 5 1 第四步:2# 从叶 2 跳至 R 上, 叶1 第五步:1# 从叶 1 跳至 R 上,
2.1 递归的概念
例5 整数划分问题 前面的几个例子中,问题本身都具有比较明显的递归关系,因 而容易用递归函数直接求解。 在本例中,如果设p(n)为正整数n的划分数,则难以找到递归关 系,因此考虑增加一个自变量:将最大加数n1不大于m的划分个 数记作q(n,m)。可以建立q(n,m)的如下递归关系。
3. 先看简单情况,河中无柱子:S = 0 , Jump ( 0 , y ) 2 ; 当 y = 1 时,Jump ( 0 , 1 ) = 说明:河中有一片荷叶,可以过两只青蛙,起始时 L 上有 两只青蛙,1# 在 2# 上面。 第一步:1# 跳到荷叶上; 第二步:2# 从 L 直接跳至 R 上; 第三步:1# 再从荷叶跳至 R 上。 如下图: 1
递归小结
优点:结构清晰,可读性强,而且容易用数学归纳法来证明算 法的正确性,因此它为设计算法、调试程序带来很大方便。 缺点:递归算法的运行效率较低,无论是耗费的计算时间还是 占用的存储空间都比非递归算法要多。
案例:青蛙过河
一条小溪尺寸不大,青蛙可以从左岸跳到右岸,在左岸有 一石柱L,面积只容得下一只青蛙落脚,同样右岸也有一石柱R, 面积也只容得下一只青蛙落脚。有一队青蛙从尺寸上一个比一个 小。我们将青蛙从小到大,用1,2,…,n编号。规定初始时这 队青蛙只能趴在左岸的石头L上,按编号一个落一个,小的落在 大的上面。不允许大的在小的上面。在小溪中有S根石柱,有y 片荷叶,规定溪中的柱子上允许一只青蛙落脚,如有多只同样要 求按编号一个落一个,大的在下,小的在上,而且必须编号相邻。 对于荷叶只允许一只青蛙落脚,不允许多只在其上。对于右岸的 石柱R,与左岸的石柱L一样允许多个青蛙落脚,但须一个落一 个,小的在上,大的在下,且编号相邻。当青蛙从左岸的L上跳 走后就不允许再跳回来;同样,从左岸L上跳至右岸R,或从溪 中荷叶或溪中石柱跳至右岸R上的青蛙也不允许再离开。问在已 知溪中有S根石柱和y片荷叶的情况下,最多能跳过多少只青蛙?