递归栈的应用
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
注:累计递归调用次数:2*Fib(n+1)-1。
(3)有的数据结构如二叉树、广义表、图等(如 树的遍历、图的搜索)定义。
例2、问题的解法是递归的: 利用递归思想来求解某类问题(本身没有
明显的递归结构,但操作方法可以用递归很好 的描述)使其更为简单。 如:汉诺塔问题、背包问题、八皇后问题等。
经典问题——汉诺塔问题
3.3递归
(栈的应用)
1. 递归的定义 2. 基本思想 3. 递归要素 3. 递归应用举例
阶乘函数 汉诺塔问题
3.3 递归
1、递归(recursion)的定义
递归:子程序(或函数)直接调用自己或通过一系列 调用语句间接调用自己,是一种描述问题和解决问题 的基本方法。
。
递归按其调用方式可分为直接递归和间接递归:
0
n=0
Fib(n)= 1
n=1
Fib(n-1)+Fib(n-2) n>1
0 1 1 2 3 5 8 13 ……
递归算法 long unsigned Fib(int n) {
if (n==0||n==1) return n;//递归出口 else return Fib(n-1)+Fib(n-2);//递归调用 }
如:求解阶乘 n! ,递归工作仅需保留4个参数: 函数名fact,参数n,局部变量x(存放fact(n-1)的值 )和y(存放n*fact(n-1)的值)。 参见P103.
6、消除递归( P103 ) 原因:递归算法时间效率较差。 (1)尾递归和单向递归算法,用带循环结构 的算法来代替。
(2)用自定义栈来模拟系统运行时的栈,保 存相关信息,用非递归算法来模拟递归算法。
3 、递归的要素
⑴ 递归边界条件:确定递归到何时终止, 也称为递归出口; ⑵ 递归模式:大问题是如何分解为小问题 的,也称为递归体。
4、递归应用举例
例1、定义是递归的: (1)阶乘函数
n!=
1
n * ( n - 1 )!
当 n=0 时 当 n≥1 时
递归算法 long unsigned fact (int n){
if(n==0) return 1; else return n*fact (n-1); }
求解阶乘 n! 的过程
计算 fact(4)
递
计算 4*fact(3)
归 调
计算 3*fact(2)
用
计算 2*fact(1)
计算 fact(1)
返回 24
返回 6
回 归
返回 2
求 值
返回1
(2)Fibonacci数列
在世界刚被创建的时候有一座钻石宝塔(塔 A),其上有64个金碟。所有碟子按从大到小的 次序从塔底堆放至塔顶。紧挨着这座塔有另外 两个钻石宝塔(塔B和塔C)。从世界创始之日 起,婆罗门的牧师们就一直在试图把塔A上的碟 子移动到塔C上去,其间借助于塔B的帮助。每 次只能移动一个碟子,任何时候都不能把一个 碟子放在比它小的碟子上面。当牧师们完成任 务时,世界末日也就到了。
1、直接递归:自己调用自己。如:
void f(n)
{……
f(n/2);
Biblioteka Baidu……
}
2、间接递归:P调用D,D调用P;
如:
void P(int n) void D(int n)
{ ……
{ ……
D(n/3);
P(n/2);
……
……
}
}
2、 递归的基本思想
问题分解:把一个不能或不好解决的大问题 转化为一个或几个小问题,再把这些小问题 进一步分解成更小的小问题,直至每个小问 题都可以直接解决。
递归过程在实现时,需要自己调用自己。 层层向下递归,返回次序正好相反:
递归调用
n! (n-1)! (n-2)!
1!=1
返回次序
递归函数的内部执行过程
每一次递归调用时,需要为过程中使用的 参数、局部变量等另外分配存储空间。
每层递归调用需分配的空间形成递归工作 记录,按后进先出的栈组织。
活动 记录 框架
Hanio(1,A,B,C) Move (A,C)
5、递归过程与递归工作栈 非递归函数的调用,在函数调用前要保存以下 三方面的信息: (1)返回地址 (2)本函数调用时,与形参结合的实参值, 包括函数名和函数参数。 (3)本函数的局部变量值。 递归函数调用也须保存这些信息,使用“递归 工作栈”来处理。 活动记录:栈顶的工作记录必定是当前正在执 行的工作记录.
实例分析: (1)尾递归:递归调用语句只有一个,且处 于函数的最后。(可不用栈来存取相关信息)
如阶乘求解的非递归算法: long unsigned fact(int n){ long unsigned int f=1; for(int i=1;i<=n;i++) f=f*i; return f; }
Hanio(1,C,A,B)
Hanio(1,A,B,C) Move (A,C)
Hanio(1,C,A,B) Move (C,B)
Move (A,C) Hanio(2,B,A,C)
结束
Hanio(2,B,A,C)
Hanio(1,B,C,A) Move (B,C)
Hanio(1,A,B,C)
Hanio(1,B,C,A) Move (B,A)
局部变量 返回地址 值参
递归 工作记录
⑴ 运行开始时,首先为递归调用建立一个工作栈,其 结构包括值参、局部变量和返回地址;
⑵ 每次执行递归调用之前,把递归函数的值参和局部 变量的当前值以及调用后的返回地址压栈;
⑶ 每次递归调用结束后,将栈顶元素出栈,使相应的 值参和局部变量恢复为调用前的值,然后转向返回地 址指定的位置继续执行。
if (n==1) Move(A, C); else {
Hanoi(n-1, A, C, B); Move(A, C); Hanoi(n-1, B, A, C); } }
汉诺塔递归调用过程: Hanio(2,A,C,B)
Hanio(3,A,B,C) Hanio(2,A,C,B)
Hanio(1,A,B,C) Move (A,B)
汉诺塔问题的递归求解方法:
如果 n = 1,则将这一个盘子直接从 塔A移到 塔 C 上。否则,执行以下三步: ⑴ 将塔A上的n-1个碟子借助塔C先移到塔B上;
⑵ 把塔A上剩下的一个碟子移到塔C上; ⑶ 将n-1个碟子从塔B借助于塔A移到塔C上。
算法实现: void Hanoi(int n, char A, char B, char C) {
(3)有的数据结构如二叉树、广义表、图等(如 树的遍历、图的搜索)定义。
例2、问题的解法是递归的: 利用递归思想来求解某类问题(本身没有
明显的递归结构,但操作方法可以用递归很好 的描述)使其更为简单。 如:汉诺塔问题、背包问题、八皇后问题等。
经典问题——汉诺塔问题
3.3递归
(栈的应用)
1. 递归的定义 2. 基本思想 3. 递归要素 3. 递归应用举例
阶乘函数 汉诺塔问题
3.3 递归
1、递归(recursion)的定义
递归:子程序(或函数)直接调用自己或通过一系列 调用语句间接调用自己,是一种描述问题和解决问题 的基本方法。
。
递归按其调用方式可分为直接递归和间接递归:
0
n=0
Fib(n)= 1
n=1
Fib(n-1)+Fib(n-2) n>1
0 1 1 2 3 5 8 13 ……
递归算法 long unsigned Fib(int n) {
if (n==0||n==1) return n;//递归出口 else return Fib(n-1)+Fib(n-2);//递归调用 }
如:求解阶乘 n! ,递归工作仅需保留4个参数: 函数名fact,参数n,局部变量x(存放fact(n-1)的值 )和y(存放n*fact(n-1)的值)。 参见P103.
6、消除递归( P103 ) 原因:递归算法时间效率较差。 (1)尾递归和单向递归算法,用带循环结构 的算法来代替。
(2)用自定义栈来模拟系统运行时的栈,保 存相关信息,用非递归算法来模拟递归算法。
3 、递归的要素
⑴ 递归边界条件:确定递归到何时终止, 也称为递归出口; ⑵ 递归模式:大问题是如何分解为小问题 的,也称为递归体。
4、递归应用举例
例1、定义是递归的: (1)阶乘函数
n!=
1
n * ( n - 1 )!
当 n=0 时 当 n≥1 时
递归算法 long unsigned fact (int n){
if(n==0) return 1; else return n*fact (n-1); }
求解阶乘 n! 的过程
计算 fact(4)
递
计算 4*fact(3)
归 调
计算 3*fact(2)
用
计算 2*fact(1)
计算 fact(1)
返回 24
返回 6
回 归
返回 2
求 值
返回1
(2)Fibonacci数列
在世界刚被创建的时候有一座钻石宝塔(塔 A),其上有64个金碟。所有碟子按从大到小的 次序从塔底堆放至塔顶。紧挨着这座塔有另外 两个钻石宝塔(塔B和塔C)。从世界创始之日 起,婆罗门的牧师们就一直在试图把塔A上的碟 子移动到塔C上去,其间借助于塔B的帮助。每 次只能移动一个碟子,任何时候都不能把一个 碟子放在比它小的碟子上面。当牧师们完成任 务时,世界末日也就到了。
1、直接递归:自己调用自己。如:
void f(n)
{……
f(n/2);
Biblioteka Baidu……
}
2、间接递归:P调用D,D调用P;
如:
void P(int n) void D(int n)
{ ……
{ ……
D(n/3);
P(n/2);
……
……
}
}
2、 递归的基本思想
问题分解:把一个不能或不好解决的大问题 转化为一个或几个小问题,再把这些小问题 进一步分解成更小的小问题,直至每个小问 题都可以直接解决。
递归过程在实现时,需要自己调用自己。 层层向下递归,返回次序正好相反:
递归调用
n! (n-1)! (n-2)!
1!=1
返回次序
递归函数的内部执行过程
每一次递归调用时,需要为过程中使用的 参数、局部变量等另外分配存储空间。
每层递归调用需分配的空间形成递归工作 记录,按后进先出的栈组织。
活动 记录 框架
Hanio(1,A,B,C) Move (A,C)
5、递归过程与递归工作栈 非递归函数的调用,在函数调用前要保存以下 三方面的信息: (1)返回地址 (2)本函数调用时,与形参结合的实参值, 包括函数名和函数参数。 (3)本函数的局部变量值。 递归函数调用也须保存这些信息,使用“递归 工作栈”来处理。 活动记录:栈顶的工作记录必定是当前正在执 行的工作记录.
实例分析: (1)尾递归:递归调用语句只有一个,且处 于函数的最后。(可不用栈来存取相关信息)
如阶乘求解的非递归算法: long unsigned fact(int n){ long unsigned int f=1; for(int i=1;i<=n;i++) f=f*i; return f; }
Hanio(1,C,A,B)
Hanio(1,A,B,C) Move (A,C)
Hanio(1,C,A,B) Move (C,B)
Move (A,C) Hanio(2,B,A,C)
结束
Hanio(2,B,A,C)
Hanio(1,B,C,A) Move (B,C)
Hanio(1,A,B,C)
Hanio(1,B,C,A) Move (B,A)
局部变量 返回地址 值参
递归 工作记录
⑴ 运行开始时,首先为递归调用建立一个工作栈,其 结构包括值参、局部变量和返回地址;
⑵ 每次执行递归调用之前,把递归函数的值参和局部 变量的当前值以及调用后的返回地址压栈;
⑶ 每次递归调用结束后,将栈顶元素出栈,使相应的 值参和局部变量恢复为调用前的值,然后转向返回地 址指定的位置继续执行。
if (n==1) Move(A, C); else {
Hanoi(n-1, A, C, B); Move(A, C); Hanoi(n-1, B, A, C); } }
汉诺塔递归调用过程: Hanio(2,A,C,B)
Hanio(3,A,B,C) Hanio(2,A,C,B)
Hanio(1,A,B,C) Move (A,B)
汉诺塔问题的递归求解方法:
如果 n = 1,则将这一个盘子直接从 塔A移到 塔 C 上。否则,执行以下三步: ⑴ 将塔A上的n-1个碟子借助塔C先移到塔B上;
⑵ 把塔A上剩下的一个碟子移到塔C上; ⑶ 将n-1个碟子从塔B借助于塔A移到塔C上。
算法实现: void Hanoi(int n, char A, char B, char C) {