ACMHPU第二讲_递归与分治策略1
合集下载
递归与分治ppt课件

C三个柱子间移动。}
2023/10/8
计算机算法设计与分析
3
Hanoi塔问题的时间复杂性
n Hanoi塔问题的时间复杂性为O(2n)。 n 证明:对n归纳证明move(n) = 2n – 1。 n 归纳基础:当n = 1, move(1) = 1 = 21 – 1。 n 归纳假设:当n k, move(n) = 2n – 1。 n 归纳步骤:当n= k + 1,移动次数为
2、除法,即n / b,的形式
2023/11/4
计算机算法设计与分析
21
递归算法的时间复杂性
n 若~为减法,即n – b,则有:
T(n) = aT(n – b) + D(n)
= a(aT(n – 2b) + D(n – b)) + D(n) =
k–1
k–1
= akT(1) + ai D(n – ib) = ak + ai D(n – ib)
n q最(n简, m单)情{ 形1:(1) q(n, 1)=1, q(1, mn)==1 n或, mm≥1=;1 n 递q(iin归ff,((mnn关)<=系==1):1q1)||(|((+|nm2(,)qmm<(qn=–(1,n1=)n,)–+1n1)q))(=rrnee–1ttmuu+rr,nqnm(01n);;, nnn>–≤1m)m,>n1>1; n 产i生f (n的=新= 情1) 况|| (:n < m) return 1 + q(n, n–1); n (3r)eqtu(nr,nmq)(n=,qm(n–,1m) +–1q)(n+–qm(,nm–m);, m} ), n>m>1 n (整4)数q(nn的, m划)分= q数(nρ,(n),=nq<(nm, n。)。
2023/10/8
计算机算法设计与分析
3
Hanoi塔问题的时间复杂性
n Hanoi塔问题的时间复杂性为O(2n)。 n 证明:对n归纳证明move(n) = 2n – 1。 n 归纳基础:当n = 1, move(1) = 1 = 21 – 1。 n 归纳假设:当n k, move(n) = 2n – 1。 n 归纳步骤:当n= k + 1,移动次数为
2、除法,即n / b,的形式
2023/11/4
计算机算法设计与分析
21
递归算法的时间复杂性
n 若~为减法,即n – b,则有:
T(n) = aT(n – b) + D(n)
= a(aT(n – 2b) + D(n – b)) + D(n) =
k–1
k–1
= akT(1) + ai D(n – ib) = ak + ai D(n – ib)
n q最(n简, m单)情{ 形1:(1) q(n, 1)=1, q(1, mn)==1 n或, mm≥1=;1 n 递q(iin归ff,((mnn关)<=系==1):1q1)||(|((+|nm2(,)qmm<(qn=–(1,n1=)n,)–+1n1)q))(=rrnee–1ttmuu+rr,nqnm(01n);;, nnn>–≤1m)m,>n1>1; n 产i生f (n的=新= 情1) 况|| (:n < m) return 1 + q(n, n–1); n (3r)eqtu(nr,nmq)(n=,qm(n–,1m) +–1q)(n+–qm(,nm–m);, m} ), n>m>1 n (整4)数q(nn的, m划)分= q数(nρ,(n),=nq<(nm, n。)。
递归与分治策略

7
例2-4 排列问题 Template<class type>
void Perm(Type list[ ], int k, int m)
{//Generate all permutations of list[k:m].
if (k==m) //第一版中有错误
{//list[k:m] has one permutation, output it
}
例2-2 Fibonacci数列
1
n=0 这两种递归可 转换为非递归
F(n)= 1
n=1 方式,但并不
F(n-1)+F(n-2) n>1 是全部递归都
int Fibonacci(int n)
能够转换。
{ if(n<=1) return 1;
return Fibonacci(n-1)+Fibonacci(n-2); }
q(n, n)=1+q(n, n-1) 若n>m>1,则 q(n, m)=q(n, m-1)+q(n-m, m)
10
例2-5 整数划分问题
int a(int n, int m) {
if((n<1) or (m<1)) return 0; if((n==1) or (m==1)) return 1; if(n<m) return q(n,n); if(n==m) return q(n,m-1)+1; return q(n,m-1)+q(n-m,m); }
第2章 递归与分治策略
Recursive divide-and-conquer strategy
• 了解递归旳概念 • 掌握设计有效算法旳分治策略 • 经过范例学习分治策略旳设计技巧
例2-4 排列问题 Template<class type>
void Perm(Type list[ ], int k, int m)
{//Generate all permutations of list[k:m].
if (k==m) //第一版中有错误
{//list[k:m] has one permutation, output it
}
例2-2 Fibonacci数列
1
n=0 这两种递归可 转换为非递归
F(n)= 1
n=1 方式,但并不
F(n-1)+F(n-2) n>1 是全部递归都
int Fibonacci(int n)
能够转换。
{ if(n<=1) return 1;
return Fibonacci(n-1)+Fibonacci(n-2); }
q(n, n)=1+q(n, n-1) 若n>m>1,则 q(n, m)=q(n, m-1)+q(n-m, m)
10
例2-5 整数划分问题
int a(int n, int m) {
if((n<1) or (m<1)) return 0; if((n==1) or (m==1)) return 1; if(n<m) return q(n,n); if(n==m) return q(n,m-1)+1; return q(n,m-1)+q(n-m,m); }
第2章 递归与分治策略
Recursive divide-and-conquer strategy
• 了解递归旳概念 • 掌握设计有效算法旳分治策略 • 经过范例学习分治策略旳设计技巧
第2章递归与分治策略

说明:边界条件与递归方程是递归函数的两个要素, 递归函数只有具备了这两个要素,才能在有限次计
算后得出结果。
2024/7/30
算法设计与分析
4
递归函数的内部执行过程
递归函数内部执行过程如下:
(1)运行开始时,为递归调用建立一个工作栈,其结 构包括实参、局部变量和返回地址;
(2)每次执行递归调用之前,把递归函数的实参和局 部变量的当前值以及调用后的返回地址压栈;
• Ackerman函数A(n, m)定义如下,n, m是两个独 立的整变量,其中n, m均≥0:
A1,0 2
A0, m 1
An,0 n 2
An, m AAn 1, m, m 1
m0 n2 n, m 1
2024/7/30
算法设计与分析
8
分析
• A(n,m)的自变量m的每一个值都定义了一个单 变量函数:
– 将求出的小规模的问题的解合并为一个更大规模的问 题的解,自底向上逐步求出原来问题的解。
2024/7/30
算法设计与分析
25
原问题 的规模是n
子问题1 的规模是n/2
子问题2 的规模是n/2
子问题1的解
子问题2的解
原问题的解
2024/7/30
算法设计与分析
26
问题(N个输入)
合 子问题1 并 解
(3)每次递归调用结束后,将栈顶元素出栈,使相应 的实参和局部变量恢复为调用前的值,然后转向返回地 址指定的位置继续执行
举例2-2:Fibonacci数列
• 无穷数列1, 1, 2, 3, 5, 8, 13, 21, 34, 55,…,被称
为Fibonacci数列。 • 它可以递归定义为:
第2章 递归与分治策略

20
实现过程
我们初始化数组p[1..n]的值为0,对于元素n,可 以依次把它放到数组的p[1],p[2],…,p[n]位臵, 在n放定一个位臵后p[k]后,剩下的n-1个元素可以 放在那些值为0的数组元素p[1..k-1]和p[k+1..n] 上。 依次递归下去,直到数组没有为0 的元素为止。 注意:在n放定一个位臵p[k],找到剩下n-1个元素 的所有排列后,在找n的下一个可放臵位臵时,即把 n放到位臵P[k+1]前,原来放n的位臵p[k]一定要臵 0。否则,将有某些元素找不到位臵。
A(n,0) = n + 2
A(n,m) = A(A(n-1,m),m-1)
n ≧2
n,m≧1
m=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,
A(1,1)=2 可以得出 A(n,1)=2*n m=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2), A(1,2)=A(A(0,2),1)=A(1,1)=2 可以得出 A(n,2)= 2n 。 m=3时,类似的可以推出
A(1,0) = 2
A(0,m) = 1
A(n,0) = n + 2 A(n,m) = A(A(n-1,m),m-1)
m ≧0
n ≧2 n,m≧1
6
A(1,0) = 2 A(0,m) = 1 Ackerman函数的特征:A(n,m)的自变量 m的每一个值 m ≧0
都定义了一个单变量函数。
m=0时,A(n,0)=n+2
T(n)= O(1) aT(n/b)+f(n) n=1 n>1
通过求解递归方程得到递归算法的时间复杂性。
26
算法分析_递归与分治策略

递归算法特点
1.递归过程一般通过函数或子过程来实现。也就 是说我们要先定义子函数或者子过程. 2.问题求解规模缩小,把问题转化为规模缩小了的 同类问题的子问题。然后递归调用函数(或过程) 来表示问题的解。 3.相邻两次重复之间有紧密的联系,前一次要为 后一次做准备(通常前一次的输出就作为后一次的 输入);
实例三 Hanoi塔
时间复杂度?
递归算法
№.递归算法定义 №.实例分析 №.递归算法特点 №.递归与非递归的转化
递归算法特点
long Fact ( int n ) { if ( n==0 ) return 1; else return n*Fact(n-1); }
Void main() {… Fact(5); …} long Fib ( int n ) { if ( n==1 ) return 1; if ( n==2 ) return 1; return Fib(n-1)+Fib(n2); } void main() {… Fib(12); … }
1.函数调用与返回的过程
(2)函数返回 从被调用函数返回调用函数之前,应该完成下 列三项任务: 保存被调函数的计算结果; 释放被调函数保存局部变量的数据区; 依照被调函数保存的返回地址将控制转移到调 用函数。
1. int main() 2. { 3. int n = 10; 4. int sn; 5. sn = sum(n); 6. cout << sn << endl; 7. }
实例三 Hanoi塔
实例三 Hanoi塔
我们定义一个过程Hanoi(N,A,B,C),表示有N 个金盘需要从A柱搬到B柱(以C柱为过渡)。那么完 成它只需3步: ①Hanoi(N一1,A,C,B)它的意思是把A柱上 的N一1个金盘搬到C柱; ② A→B 它的意思是把一个(最大的)金盘从A柱 搬到B柱; ③ Hanoi(N-1,C,B,A)它的意思是把c柱上 的N一1个金盘搬到B柱。
算法设计与分析:第02章 递归与分治策略

1 5 n1 1 5 n1 1 F (n) 2 5 2
但本例中的Ackerman函数却无法找到非递归的定义。
2.1
例3 Ackerman函数
递归的概念
• A(n,m)的自变量m的每一个值都定义了一个单变量函数: • M=0时,A(n,0)=n+2 • M=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,和A(1,1)=2故 A(n,1)=2*n • M=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2),和 A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2^n 。
2.1
递归的概念
例5 整数划分问题 将正整数n表示成一系列正整数之和:n=n1+n2+…+nk, 其中n1≥n2≥…≥nk≥1,k≥1。 正整数n的这种表示称为正整数n的划分。求正整数n的不 同划分个数。 例如正整数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。
下面来看几个实例。
2.1
递归的概念
边界条件
例1 阶乘函数 阶乘函数可递归地定义为:
n0 1 n! n(n 1)! n 0
递归方程 边界条件与递归方程是递归函数的二个要素,递归函 数只有具备了这两个要素,才能在有限次计算后得出 结果。
但本例中的Ackerman函数却无法找到非递归的定义。
2.1
例3 Ackerman函数
递归的概念
• A(n,m)的自变量m的每一个值都定义了一个单变量函数: • M=0时,A(n,0)=n+2 • M=1时,A(n,1)=A(A(n-1,1),0)=A(n-1,1)+2,和A(1,1)=2故 A(n,1)=2*n • M=2时,A(n,2)=A(A(n-1,2),1)=2A(n-1,2),和 A(1,2)=A(A(0,2),1)=A(1,1)=2,故A(n,2)= 2^n 。
2.1
递归的概念
例5 整数划分问题 将正整数n表示成一系列正整数之和:n=n1+n2+…+nk, 其中n1≥n2≥…≥nk≥1,k≥1。 正整数n的这种表示称为正整数n的划分。求正整数n的不 同划分个数。 例如正整数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。
下面来看几个实例。
2.1
递归的概念
边界条件
例1 阶乘函数 阶乘函数可递归地定义为:
n0 1 n! n(n 1)! n 0
递归方程 边界条件与递归方程是递归函数的二个要素,递归函 数只有具备了这两个要素,才能在有限次计算后得出 结果。
第2讲 递归与分治策略
第2讲 递归与分治策略
递归的概念 分治法的基本思想
分治法范例学习
第2讲 递归与分治策略
2.1 递归的概念
• 直接或间接地调用自身的较小模式 的算法称为递归算法。
• 用函数自身的较小模式给出其定义 的函数称为递归函数。
第2讲 递归与分治策略
递归算法的特点
• 递归就是在过程或函数里调用自身。 • 在使用递归策略时,必须有一个明确的递归结 束条件,称为递归出口。 • 递归算法解题通常显得很简洁,但递归算法解 题的运行效率较低。所以一般不提倡用递归算 法设计程序。 • 在递归调用的过程当中系统为每一层的返回点、 局部量等开辟了栈来存储。递归次数过多容易 造成栈溢出等,所以一般不提倡用递归算法设 计程序。
第2讲 递归与分治策略
• 有趣的兔子问题
•
一般而言,兔子在出生两个月后,就有 繁殖能力,一对兔子每个月能生出一对 小兔子来。如果所有兔子都不死,那么 一年以后可以繁殖多少对兔子?
第2讲 递归与分治策略
• 分析如下: 第一个月小兔子没有繁殖能力,所以还是一对; 两个月后,生下一对小兔子,总数共有两对; 三个月以后,老兔子又生下一对,因为小兔子还没有繁殖 能力,总数共是三对; …… 依次类推可以列出下表:
例6
Hanoi塔问题
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘, 这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号 为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍 按同样顺序叠臵。在移动圆盘时应遵守以下移动规则: 规则1:每次只能移动1个圆盘; 规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上; 规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任 一塔座上。
递归的概念 分治法的基本思想
分治法范例学习
第2讲 递归与分治策略
2.1 递归的概念
• 直接或间接地调用自身的较小模式 的算法称为递归算法。
• 用函数自身的较小模式给出其定义 的函数称为递归函数。
第2讲 递归与分治策略
递归算法的特点
• 递归就是在过程或函数里调用自身。 • 在使用递归策略时,必须有一个明确的递归结 束条件,称为递归出口。 • 递归算法解题通常显得很简洁,但递归算法解 题的运行效率较低。所以一般不提倡用递归算 法设计程序。 • 在递归调用的过程当中系统为每一层的返回点、 局部量等开辟了栈来存储。递归次数过多容易 造成栈溢出等,所以一般不提倡用递归算法设 计程序。
第2讲 递归与分治策略
• 有趣的兔子问题
•
一般而言,兔子在出生两个月后,就有 繁殖能力,一对兔子每个月能生出一对 小兔子来。如果所有兔子都不死,那么 一年以后可以繁殖多少对兔子?
第2讲 递归与分治策略
• 分析如下: 第一个月小兔子没有繁殖能力,所以还是一对; 两个月后,生下一对小兔子,总数共有两对; 三个月以后,老兔子又生下一对,因为小兔子还没有繁殖 能力,总数共是三对; …… 依次类推可以列出下表:
例6
Hanoi塔问题
设a,b,c是3个塔座。开始时,在塔座a上有一叠共n个圆盘, 这些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号 为1,2,…,n,现要求将塔座a上的这一叠圆盘移到塔座b上,并仍 按同样顺序叠臵。在移动圆盘时应遵守以下移动规则: 规则1:每次只能移动1个圆盘; 规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上; 规则3:在满足移动规则1和2的前提下,可将圆盘移至a,b,c中任 一塔座上。
第2章递归与分治策略.
2.2 分治法的基本思想
分治法的基本思想
分治法的基本思想是将一个规模为n的问题分
解为k个规模较小的子问题,这些子问题互相 独立且与原问题相同。 对这k个子问题分别求解。如果子问题的规模 仍然不够小,则再划分为k个子问题,如此递 归的进行下去,直到问题规模足够小,很容易 求出其解为止。 将求出的小规模的问题的解合并为一个更大规 模的问题的解,自底向上逐步求出原来问题的 解。
二分搜索实例:设在A[8]中顺序放了以下9个元素:
A[0] A[1] A[2] A[3] A[4] A[5] A[6] A[7] A[8]
-15 -6
③
0
③
7
④
9
①
23
54
②
82 101
③ ④
②
搜索x=9 9= =A[4], 一次比较,成功, 最好情况 搜索x=-15, -15<A[4], -15<A[1], -15= =A[0], 3次比较, 成功 搜索x=101 101>A[4], 101>A[6], 101>A[7], 101= =A[8], 4次比较, 成功 搜索x=8 8<A[4], 8>A[1], 8>A[2], 8>A[3], 4次比较, 不成功检索
1 5 1 F (n) 2 5
1 5 2
n 1
递归小结
优点:结构清晰,可读性强,而且容易用数学 归纳法来证明算法的正确性,因此它为设计算 法、调试程序带来很大方便。
缺点:递归算法的运行效率较低,无论是耗费 的计算时间还是占用的存储空间都比非递归算 法要多。
边界条件
1 n0 F ( n) 1 n 1 F (n 1) F (n 2) n 1
递归与分治策略讲义课件(ppt 65页)
int prev,now,next,j; if (n<=1) return(1);
else {
prev=1; now=1; for(j=2;j<=n;j++) {
next=prev+now; prev=now; now=next; } return(next); } }
具有编译递归程序能力的程 序设计语言有:C、Pascal 、 ALGOL、 PL/A 、 ADA、 QBASIC等,不具有编译递归 程序能力的程序设计语言有: FORTRAN、 COBOL、BASIC、 低级语言。
Hanio(3,A,B,C) Hanio(2,A,C,B)
Move (A,C)
Hanio(2,B,A,C)
结束
Hanio(2,A,C,B)
Hanio(1,A,B,C) Move (A,B)
Hanio(1,C,A,B)
Hanio(1,A,B,C) Move (A,C)
Hanio(1,C,A,B) Move (C,B)
例3 Hanoi塔问题
设x,y,z是3个塔座。开始时,在塔座x上有一叠共n个圆盘,这 些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号 为1,2,…,n,现要求将塔座x上的这一叠圆盘移到塔座z上,并仍 按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则1:每次只能移动1个圆盘;
规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
(1)运行开始时,首先为递归调用建立一个工作栈,其结 构包括值参、局部变量和返回地址;
(2)每次执行递归调用之前,把递归函数的值参和局部变 量的当前值以及调用后的返回地址压栈;
(3)每次递归调用结束后,将栈顶元素出栈,使相应的值 参和局部变量恢复为调用前的值,然后转向返回地址指定 的位置继续执行。
else {
prev=1; now=1; for(j=2;j<=n;j++) {
next=prev+now; prev=now; now=next; } return(next); } }
具有编译递归程序能力的程 序设计语言有:C、Pascal 、 ALGOL、 PL/A 、 ADA、 QBASIC等,不具有编译递归 程序能力的程序设计语言有: FORTRAN、 COBOL、BASIC、 低级语言。
Hanio(3,A,B,C) Hanio(2,A,C,B)
Move (A,C)
Hanio(2,B,A,C)
结束
Hanio(2,A,C,B)
Hanio(1,A,B,C) Move (A,B)
Hanio(1,C,A,B)
Hanio(1,A,B,C) Move (A,C)
Hanio(1,C,A,B) Move (C,B)
例3 Hanoi塔问题
设x,y,z是3个塔座。开始时,在塔座x上有一叠共n个圆盘,这 些圆盘自下而上,由大到小地叠在一起。各圆盘从小到大编号 为1,2,…,n,现要求将塔座x上的这一叠圆盘移到塔座z上,并仍 按同样顺序叠置。在移动圆盘时应遵守以下移动规则:
规则1:每次只能移动1个圆盘;
规则2:任何时刻都不允许将较大的圆盘压在较小的圆盘之上;
(1)运行开始时,首先为递归调用建立一个工作栈,其结 构包括值参、局部变量和返回地址;
(2)每次执行递归调用之前,把递归函数的值参和局部变 量的当前值以及调用后的返回地址压栈;
(3)每次递归调用结束后,将栈顶元素出栈,使相应的值 参和局部变量恢复为调用前的值,然后转向返回地址指定 的位置继续执行。
第二章递归与分治策略
缺点:递归算法的运行效率较低,无论是耗费的 计算时间还是占用的存储空间都比非递归算法要 多。
分治法——总体思想
• 思想:对一个规模为n的问题,若该问题可以容易地
解决(比方说规模较小),则直接解决,否则将其分解为 k个规模较小的子问题,这些子问题相互独立且与原问 题形式相同。分治策略递归地分解这些子问题,然后 将各个子问题的解合并得到原问题的解。
swap(list, k, i);
swap(list, 0, 2)
}
2014-11-13
第二个swap的目的是将数组恢复成原始情况,若不恢复,再 下次执行循环体的时候会出现重复排列。
例5:二叉树的遍历
二叉树的遍历方法有先序遍历、中序遍历和后序遍历。 以先序遍历为例,对应的二叉树访问顺序是根节点、左子 树、右子树,然后对左子树和右子树也采用同样的先序遍 历方法。 • 动画展示 • 相关代码
的下降;
②分解后的子问题和原问题的形式相同,这样有
利于使用递归算法,方便求解。
分治法——基本步骤
1. 分解:将原问题分解为若干个规模较小、相互独立、
与原问题形式相同的子问题。
2. 解决:若子问题规模较小,则直接解决,否则递归地 利用分治法解各个子问题。 3. 合并:将各个子问题的解合并为原问题的解。
// 交换list[k]与list[i]
0
1 2
2 1
3 4
swap(list, 0, 2)
3
// 计算list[k+1:m]全排列
perm(list, k + 1, m);
首元素已经确定为3,对后三个元素 进行全排列,即perm(list, 1, 3),排
列完成后返回
0 1
分治法——总体思想
• 思想:对一个规模为n的问题,若该问题可以容易地
解决(比方说规模较小),则直接解决,否则将其分解为 k个规模较小的子问题,这些子问题相互独立且与原问 题形式相同。分治策略递归地分解这些子问题,然后 将各个子问题的解合并得到原问题的解。
swap(list, k, i);
swap(list, 0, 2)
}
2014-11-13
第二个swap的目的是将数组恢复成原始情况,若不恢复,再 下次执行循环体的时候会出现重复排列。
例5:二叉树的遍历
二叉树的遍历方法有先序遍历、中序遍历和后序遍历。 以先序遍历为例,对应的二叉树访问顺序是根节点、左子 树、右子树,然后对左子树和右子树也采用同样的先序遍 历方法。 • 动画展示 • 相关代码
的下降;
②分解后的子问题和原问题的形式相同,这样有
利于使用递归算法,方便求解。
分治法——基本步骤
1. 分解:将原问题分解为若干个规模较小、相互独立、
与原问题形式相同的子问题。
2. 解决:若子问题规模较小,则直接解决,否则递归地 利用分治法解各个子问题。 3. 合并:将各个子问题的解合并为原问题的解。
// 交换list[k]与list[i]
0
1 2
2 1
3 4
swap(list, 0, 2)
3
// 计算list[k+1:m]全排列
perm(list, k + 1, m);
首元素已经确定为3,对后三个元素 进行全排列,即perm(list, 1, 3),排
列完成后返回
0 1
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.函数调用与返回的过程
(2)函数返回 从被调用函数返回调用函数之前,应该完成下 列三项任务: 保存被调函数的计算结果; 释放被调函数保存局部变量的数据区; 依照被调函数保存的返回地址将控制转移到调 用函数。
1. int main() 2. { 3. int n = 10; 4. int sn; 5. sn = sum(n); 6. cout << sn << endl; 7. }
实例二 Fibonacci
“兔子问题”很容易列出一条递推式而得到解决。 假设第N个月的兔子数目是Fib(N),我们有:
1, n 1; Fib(n) 1, n 2; Fib(n 1) Fib(n 2), n 2.
每月的大兔子数目一定等于上月的兔子总数,而 每个月的小兔子数目一定等于上月的大兔子数目 (即前一个月的兔子的数目)。
实例三 Hanoi塔
时间复杂度?
递归算法
№.递归算法定义 №.实例分析 №.递归算法特点 №.递归与非递归的转化
递归算法特点
long Fact ( int n ) { if ( n==0 ) return 1; else return n*Fact(n-1); }
Void main() {… Fact(5); …} long Fib ( int n ) { if ( n==1 ) return 1; if ( n==2 ) return 1; return Fib(n-1)+Fib(n-2); } void main() {… Fib(12); … }
ACM应用
递归与分治
Recursion algorithm Divided-and-conquer
算法总体思想
• 对k个子问题分别求解。如果子问题的规模仍 然不够小,则再划分为k个子问题,如此递归 的进行下去,直到问题规模足够小,很容易求 出其解为止。
T(n)
=
n
T(n/2)
T(n/2)
T(n/2)
实例二 Fibonacci
1月 小兔 大兔 合计 2月 1 1 3月 1 4月 5月 6月 7月 8月 9月 10月 11月 12月
1 1 1
1
1 1
1 2
1 1 2
1 2 3
1 2 3
2 3 5
2 3 5
3 5 8
3 5 5 8 8 1313 2121 34 5555 34 5 8 8 13 21 213434 55 8989 13 55 8 1313 21 3434 5555 89 144 21 89 144
T(n/2)
算法总体思想
• 将求出的小规模的问题的解合并为一个更大规模的问 题的解,自底向上逐步求出原来问题的解。
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
这个表格虽然解决了斐波那契的兔子问题(年底时兔子的总数是144只), 但仔细观察一下这个表格,你会发现兔子的数目增长得越来越快,如果 时间再长,只用列表的方法就会有困难。(例如,你愿意用列表的方法求 出10年后兔子的数目吗?)我们需要研究表中的规律,找出一般的方法, 去解决这个问题。 仔细研究上述表格,你有些什么发现?每一个月份的大兔数、小兔数与 上一个月的数字有什么联系,能肯定这个规律吗?恭喜你,你快成功了?
1, n 0, Fact(n) n Fact(n 1), n 0.
n=4 n=3 n=2 n=1 n=0
{
if ( n==0 ) return 1; else return n*Fact(n-1); }
Void main() {… Fact(5); …}
实例一:阶乘函数
实例三 Hanoi塔
相传19世纪末,古印度的布喇玛婆罗门神庙的传 教士玩一种被称为汉诺塔的游戏:有三个木柱A、 B、C和64个大小个异,能套进木柱的盘子;开始 时64个盘子全套在A柱上,且小的在大的上面,形 成塔状;64个盘子由小到大编号分别为1、2、 3„„64。游戏要求把A柱上的盘子搬到C柱上,每 次只能移动最上层的一个盘子。移动过程中可以 利用B柱,但任何一柱都不允许大盘在小盘上面。
实例二 Fibonacci
实例二:
计算Fibonacci数列: 0,1,1,2,3,5,8,13,21,…
1, n 1, Fib(n) 1, n 2, Fib(n 1) Fib(n 2), n 2.
long Fib ( int n ) { if ( n==1 ) return 1; if ( n==2 ) return 1; return Fib(n-1)+Fib(n2); } void main() { Fib(12); }
函数f在执行中,又调用函数f自身,这称为直接递归;若 函数f在执行中,调用函数 g,而g在执行中,又调用函数 f,这称为间接递归。在实际应用中,多为直接递归,也 常简称为递归。
递归算法
№.递归算法定义 №.实例分析 №.递归算法特点 №.递归与非递归的转化
实例一:阶乘函数
计算n!(问题具有递归的数学定义). long Fact ( int n )
8. int sum ( int n ) 9. { 10. int i, s = 0; 11. for( i=1; i<n; i++) 12. s += i; 13. return s; 14.}
sum() main()
sum:i=11 sum:s=0 sum:s=55 sum:i= goto: 5 sum:n=10 main:sn=55 main:sn= main:n=10
阶乘函数可递归地定义为:
递归方程
边界条件
n0 1 n! 数的二个要素,递归函 数只有具备了这两个要素,才能在有限次计算后得出 结果。
实例二 Fibonacci
著名的意大利数学家斐波那契(Fibonacci)在他 的著作《算盘书》中提出了一个“兔子问题”: 假定小兔子一个月就可以长成大兔子,而大兔 子每个月都会生出一对小兔子。如果年初养了 一对小兔子,问到年底时将有多少对兔 子? (当然得假设兔子没有死亡而且严格按照 上述规律长大与繁殖)
为什么消除递归? 某些语言不支持函数的递归调用。 在某些关键部分,递归算法影响了执行的效率。
为了保证递归调用的正确性,需要保存调用点的现场(返回地址、 局部变量、被调用函数的参数等),以便正确地返回,并且按先进后 出的原则来管理这些信息。在高级语言(编译程序)中,是通过利用 “递归工作栈”来实现递归调用的。 f(n) f(n-1) f(n-2) f(1) f(0)
实例三 Hanoi塔
public static void hanoi(int n, int a, int b, int c) 当n=1时,问题比较简单。此时,只要将编号为1的圆盘从塔座a直 在问题规模较大时,较难找到一般的方法,因此我们尝试 接移至塔座b上即可。 用递归技术来解决这个问题。 { 当n>1时,需要利用塔座c作为辅助塔座。此时若能设法将n-1个 if (n > 0) 较小的圆盘依照移动规则从塔座a移至塔座c,然后,将剩下的最 { 大圆盘从塔座a移至塔座b,最后,再设法将n-1个较小的圆盘依照 hanoi(n-1, a, c, b); 移动规则从塔座c移至塔座b。 move(a,b); 由此可见,n个圆盘的移动问题可分为2次n-1个圆盘的移动问题, hanoi(n-1, c, b, a); 这又可以递归地用上述方法来做。由此可以设计出解Hanoi塔问题 的递归算法如下。 } }
第二讲 递归与分治 Recursion&Divided-and-conquer
ACM算法设计与分析——王建芳 wangjianfang@
公共邮箱:hpuacm@ 平时作业提交到王建芳的qq邮箱
知识提要
总体思想 递归算法 分治策略
ACM应用
内容提要
总体思想 递归算法 分治策略
递归算法特点
4.是否收敛,即终止条件。在问题的规模极小时 必须用直接给出解答而不再进行递归调用,因而 每次递归调用都是有条件的(以规模未达到直接解 答的大小为条件),无条件递归调用将会成为死循 环而不能正常结束。
递归算法
№.递归算法定义 №.实例分析 №.递归算法特点 №.递归与非递归的转化
递归与非递归的转化
实例二 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
递归方程
第n个Fibonacci数可递归地计算如下: public static int fibonacci(int n) { if (n <= 1) return 1; return fibonacci(n-1)+fibonacci(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
递归与分治
由分治法产生的子问题往往是原问题的较小模式, 这就为使用递归技术提供了方便。在这种情况下, 反复应用分治手段,可以使子问题与原问题类型一 致而其规模却不断缩小,最终使子问题缩小到很容 易直接求出其解。这自然导致递归过程的产生。 分治与递归像一对孪生兄弟,经常同时应用在算法 设计之中,并由此产生许多高效算法。