第5章_递归
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
退 出
9
递归设计步骤
(1)对原问题f(s)进行分析,假设出合理的 “较小问题” f(s’); (2)假设f(s’)是可解的,在此基础上确定 f(s)的解,即给出f(s)与f(s’)的关系; (3)确定一个特定情况(f(1)或f(0))的解, 由此作为递归出口。
首 页
上一页
下一页
返 回
退 出
10
递归程序设计具有以下两个特点: (1)具备递归出口。递归出口定义了递归的终止条 件,当程序的执行使它得到满足时,递归执行过程便 终止。有些问题的递归程序可能存在几个递归出口;
Move Disk i from Peg X to Peg Y
这样,汉诺塔问题的递归算法可设计如下:
首 页
上一页
下一页
返 回
退 出
25
void towers(int n, char fromPeg, char toPeg, char auxPeg) { if(n==1) //递归出口
{ printf("%s%c%s%c\n", "move disk 1 from peg ", fromPeg, " to peg ", toPeg); return; } //把n-1个圆盘从fromPeg借助toPeg移至auxPeg towers(n-1,fromPeg,auxPeg,toPeg);
首 页
上一页
下一页
返 回
退 出
26
测试主函数如下:
#include <stdio.h>
void main(void)
{
Towers(4, 'A', „B', „C');
}
程序运行的输出信息如下:
首 页 上一页 下一页 返 回 退 出
27
Move Disk 1 from Peg A to Peg B Move Disk 2 from Peg A to Peg C Move Disk 1 from Peg B to Peg C Move Disk 3 from Peg A to Peg B Move Disk 1 from Peg C to Peg A Move Disk 2 from Peg C to Peg B Move Disk 1 from Peg A to Peg B
首 页
上一页
下一页
返 回
退 出
24
算法设计:首先,盘子的个数n是必须的一个输 入参数,对n个盘子,我们可从上至下依次编号为 1,2,„,n;其次,输入参数还需有3个柱子的代号, 我们令3个柱子的参数名分别为fromPeg,auxPeg 和toPeg;最后,汉诺塔问题的求解是一个处理过 程,因此算法的输出是n个盘子从柱子fromPeg借 助柱子auxPeg移动到柱子toPeg的移动步骤,我们 设计每一步的移动为屏幕显示如下形式的信息:
⑶ 每次递归调用结束后,将栈顶元素出栈,使相应 局部变量 的值参和局部变量恢复为调用前的值,然后转向返 活动 递归 返回地址 回地址指定的位置继续执行。工作记录 记录
框架
首 页
值
参
上一页 下一页 返 回 退 出
21
递归函数的运行轨迹
⑴ 写出函数当前调用层执行的各语句,并用有向弧 表示语句的执行次序; ⑵ 对函数的每个递归调用,写出对应的函数调用, 从调用处画一条有向弧指向被调用函数入口,表 示调用路线,从被调用函数末尾处画一条有向弧 指向调用语句的下面,表示返回路线; ⑶ 在返回路线上标出本层调用所得的函数值。
8
该问题的算法为:
int Fibona ( int n ) { int m; if (n= =1) return (1); else if (n= =2) return(1); else { m=Fibona(n-1)+ Fibona(n-2); return (m); } }
首 页
上一页
下一页
返 回
退 出
18
首 页
上一页
下一页
返 回
退 出
19
void Hanoi(int n, char A, char B, char C) { if (n==1) Move(A, C); else { Hanoi(n-1, A, C, B); Move(A, C); Hanoi(n-1, B, A, C); } }
(2)在不满足递归出口的情况下,根据所求解问题 的性质,将原问题分解成若干子问题,这些子问题的 结构与原问题的结构相同,但规模较原问题小。子问 题的求解通过以一定的方式修改参数进行函数自身调 用加以实现,然后将子问题的解组合成原问题的解。 递归调用时,参数的修改最终必须保证递归出口得以 满足。
首 页 上一页 下一页 返 回 退 出
Move (A,C)
Hanio(2,B,A,C) Hanio(1,B,C,A)
Hanio(1,B,C,A) Move (B,A)
Hanio(2,B,A,C)
Move (B,C)
Hanio(1,A,B,C)
Hanio(1,A,B,C) Move (A,C)
结束 首 页 上一页 下一页 返源自文库回 退 出
23
在一个函数的定义中出现了对自己本身的调用, 称之为直接递归;或者一个函数p的定义中包含了对 函数q的调用,而q的实现过程又调用了p,即函数 调用形成了一个环状调用链, 这种方式称之为间接递 归。递归技术在算法和程序设计中是一种十分有用 的技术,许多高级程序设计语言均提供了支持递归 定义的机制和手段。
首 页
上一页
下一页
返 回
退 出
22
Hanio(2,A,C,B) Hanio(1,A,B,C) Hanio(3,A,B,C) Hanio(2,A,C,B) Move (A,B) Hanio(1,C,A,B)
Hanio(1,A,B,C)
Move (A,C)
Hanio(1,C,A,B) Move (C,B)
也就是所描述问题的最简单的、可解的 情况,它本身不再使用递归的定义。
首 页 上一页 下一页 返 回 退 出
5
递归算法解题通常有三个步骤:
1)分析问题、寻找递归关系:找出大规模问 题与小规模问题的关系,这样通过递归使问题 的规模逐渐变小。
2)设置边界、控制递归:找出停止条件,即 算法可解的最小规模问题。 3)设计函数、确定参数:和其它算法模块一 样设计函数体中的操作及相关参数。
//把圆盘n由fromPeg直接移至toPeg printf("%s%d%s%c%s%c\n", "move disk ", n, " from peg ", fromPeg, " to peg ", toPeg);
//把n-1个圆盘从auxPeg借助fromPeg移至toPeg
towers(n-1,auxPeg,toPeg,fromPeg); }
首 页 上一页 下一页 返 回 退 出
17
汉诺塔问题的递归求解:
如果 n = 1,则将这一个盘子直接从 塔A移到塔 C 上。否则,执行以下三步:
⑴ 将塔A上的n-1个碟子借助塔C先移到塔B上;
⑵ 把塔A上剩下的一个碟子移到塔C上;
⑶ 将n-1个碟子从塔B借助于塔A移到塔C上。
首 页
上一页
下一页
返 回
首 页
上一页
下一页
返 回
退 出
20
递归函数的内部执行过程
⑴ 每一次递归调用时,需要为过程中使用的 运行开始时,首先为递归调用建立一个工作栈, 其结构包括值参、局部变量和返回地址; 参数、局部变量等另外分配存储空间。 ⑵ 每层递归调用需分配的空间形成递归工作 每次执行递归调用之前,把递归函数的值参和局 部变量的当前值以及调用后的返回地址压栈; 记录,按后进先出的栈组织。
11
适用递归技术的问题
以下三种情况常常用到递归方法。
定义是递归的 数据结构是递归的 问题的解法是递归的
首 页
上一页
下一页
返 回
退 出
12
定义是递归的
例如,阶乘函数 1, 当n 0 时 n! n (n 1)!, 当 n 1时 求解阶乘函数的递归算法
long Factorial ( long n ) { if ( n == 0 ) return 1; else return n * Factorial (n-1); }
首 页 上一页 下一页 返 回 退 出
6
5.1 递归与递归程序设计
例1 试编一个递归函数,求正整数n的阶乘值n!。 用fact(n)表示n的阶乘值,据阶乘的数学定义可知: 1 n=0 fact(n) = n*fact(n-1) n>0
首 页
上一页
下一页
返 回
退 出
7
该问题的算法为: int Fact ( int n ) { int m; if (n= =0) return(1); else { m=n*Fact(n-1); return(m); } }
首 页
上一页
下一页
返 回
退 出
3
栈的应用举例—递归
1 递归的定义
子程序(或函数)直接调用自己或通过一系列调 用语句间接调用自己,是一种描述问题和解决问 题的基本方法。
2 递归的基本思想
问题分解:把一个不能或不好解决的大问题转化 为一个或几个小问题,再把这些小问题进一步分 解成更小的小问题,直至每个小问题都可以直接 解决。
例2 试编一个递归函数,求第n项Fibonacci级数的值。 假设使用Fibona(n)表示第n项Fibonacci级数的值, 根据Fibonacci级数的计算公式: 1 n=1 Fibona(n)= 1 n=2 Fibona(n-1)+ Fibona(n-2) n>2
首 页 上一页 下一页 返 回 退 出
首 页
a2 f
上一页
a3 f
下一页
a4 f
返 回 退 出
15
问题的解法是递归的
例如,汉诺塔(Tower of Hanoi)问题的解法: 如果 n = 1,则将这一个盘子直接从 A 柱移到 C 柱上。否则,执行以下三步: ① 用 C 柱做过渡,将 A 柱上的 (n-1) 个盘子移 到 B 柱上: ② 将 A 柱上最后一个盘子直接移到 C 柱上; ③ 用 A 柱做过渡,将 B 柱上的 (n-1) 个盘子 移到 C 柱上。
高等学校精品课程
(第2版) (第2版)
李云清
杨庆红
揭安全
人民邮电出版社
首 页 上一页 下一页 返 回 退 出
1
第5章 递 归
递归与递归程序设计
递归程序执行过程的分析 递归程序到非递归程序的转换 递归程序设计的应用实例 快速排序的递归与非递归实现
首 页
上一页
下一页
返 回
退 出
2
5.1 递归与递归程序设计
首 页 上一页 下一页 返 回 退 出
16
递归的经典问题——汉诺塔问题
在世界刚被创建的时候有一座钻石宝塔(塔A ),其上有64个金碟。所有碟子按从大到小的次序 从塔底堆放至塔顶。紧挨着这座塔有另外两个钻石 宝塔(塔B和塔C)。从世界创始之日起,婆罗门的 牧师们就一直在试图把塔A上的碟子移动到塔C上去 ,其间借助于塔B的帮助。每次只能移动一个碟子, 任何时候都不能把一个碟子放在比它小的碟子上面 。当牧师们完成任务时,世界末日也就到了。
首 页 上一页 下一页 返 回
退 出
13
数据结构是递归的
例如,单链表结构
f f
一个结点,它的指针域为NULL,是一个 单链表; 一个结点,它的指针域指向单链表,仍是 一个单链表。
首 页 上一页 下一页 返 回 退 出
14
数据结构是递归的
搜索链表最后一个结点并打印其数值 void Print ( ListNode *f ) { if ( f ->link == NULL ) printf(“%d\n”, f ->data); else Print ( f ->link ); } 递归找链尾 f a0 f a1
例 设计模拟汉诺塔问题求解过程的算法。汉诺塔问题的描述 是:设有3根标号为A,B,C的柱子,在A柱上放着n个盘子,每 一个都比下面的略小一点,要求把A柱上的盘子全部移到C柱上, 移动的规则是:(1)一次只能移动一个盘子;(2)移动过程 中大盘子不能放在小盘子上面;(3)在移动过程中盘子可以放 在A,B,C的任意一个柱子上。 问题分析:可以用递归方法求解n个盘子的汉诺塔问题。 基本思想:1个盘子的汉诺塔问题可直接移动。n个盘子的汉诺 塔问题可递归表示为,首先把上边的n-1个盘子从A柱移到B柱, 然后把最下边的一个盘子从A柱移到C柱,最后把移到B柱的n-1 个盘子再移到C柱。4个盘子汉诺塔问题的递归求解示意图如图 6-4所示。
首 页 上一页 下一页 返 回 退 出
4
递归的关键在于找出递归方程 式和递归终止条件。
递归定义:使问题向边界条件转化的规 则。递归定义必须能使问题越来越简单, 规模越来越小。 递归边界条件:必定要有一个明确的结束递
归的条件,否则递归将会无止境地进行下去,直到耗 尽系统资源。也就是说必须要某个终止递归的条件。