第四章 递归程序设计
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
if (disk_num == 1) { // 仅有一个盘时,直接从from柱移到to柱 cout << "Move disk1 from " << from << " to " << to << "\n";
} else { // 将disk_num - 1个盘从from柱移到aux柱,借助于to柱 move_tower(disk_num - 1, from, aux, to); // 将最下的盘从from柱移到to柱 cout << "Move disk" << disk_num << " from " << from << " to " << to << "\n"; // 将disk_num - 1个盘从aux柱移到to柱,借助于from柱 move_tower(disk_num - 1, aux, to, from);
其中1和3的解决方法与问题本身的解决方法相同,只 是盘数和柱子不同,因此可使用递归技术
注意:在使用递归技术时,多数情况下是解决问题的 方法相同,而处理的数据不同,这时就可将那些不同 的数据作为递归函数的参数。
14
解决汉诺塔问题的源代码:
// 函数: move_tower // 功能: 将disk_num个盘从from柱移到to柱,可以借助aux柱 void move_tower(int disk_num, char from, char to, char aux) {
游戏规则:
•将所有的盘从A柱移到C 柱,在移动过程中可以借 助另一个B柱 •每次只能移动柱最上面 的一个盘 •任何盘都不得放在比它 小的盘上
13
分析
汉诺塔问题看似复杂,但仔细分析一下,要将n个盘 从A柱移到C柱,可将此问题分解为三个子问题:
1. 将n-1个盘从A柱移到B柱 2. 将第n个盘从A柱移到C柱 3. 将n-1个盘从B柱移到C柱
递归函数的一般形式:
if (最易解决的情况) // 基础分支
解决语句
else
// 递归分支
递归函数调用
6
1、递归程序设计概述(续)
递归程序的可终止性:
正确的递归程序必须是可终止的 递归算法的基础分支通常就是终止分支 所有递归程序都必须至少拥有一个基础分
支,而且必须确保它们最终会达到某个基础 分支;否则,程序将永远运行下去,直到程 序缺少内存或者栈空间
每次递归使得问题规模变小,变得更容易解决 每个递归算法必须有一个基础分支和一个递归分支
基础分支(基线条件,base case):最容易解决的
情况,递归程序的最底层位置。在此位置时没有必 要再进行操作,可以直接返回一个结果。 递归分支(general case):普遍情况
5
1、递归程序设计概述(续)
if (n < 2) result = 1; //基础分支 else result = fibonacci(n - 1) + fibonacci(n - 2); //递归分支 return result; }
9
[例]:求1到n之间整数的和
int Summation (int n )
// 用递归方式计算1到n的和:将1到(n-1)的和加上n
第四章 递归程序设计
参见教材4.9节
1
主要内容及要求
主要内容
递归程序设计概述 简单递归程序 汉诺塔问题 递归程序的内部调用栈实现方式
要求
重点掌握递归程序设计的基本方法,以及递归程序 的执行过程
了解递归程序的内部调用栈实现方式
2
1、递归程序设计概述
递归是计算机科学中很重要的技术,例如,表达式 可以非形式化地定义为:
} return; }
15
ห้องสมุดไป่ตู้
move_tower函数的调用:
int main() {
// 将4个盘从A柱移到C柱,移动时利用B柱 move_tower(4, 'A', 'C', 'B'); return 0; }
16
4、递归程序的内部调用栈实现方 式 :fibonacci函数的执行分析
调用函数时,系统会分配一块内存保存与函数 调用相关的数据,包括:函数的返回地址、形 式参数、局部变量和返回值等
之前)
出现在 fibonacci(2) 中else部分 “+”之前的
返回值
1 fibonacci(2),前 fibonacci(3),前
fibonacci(4) main()
24
输出:
43210
fibonacci(1)返回 到fibonacci(2)的 else部分(“+”之 前),因而继续
调用 fibonacci(0), fibonacci(0)的相 关数据入栈
return result;
}
void main()
{
cout<<fibonacci(4);
}
18
首先main函 数的相关数
据入栈
main()
注:这是简化的堆 栈示意图,用函数 的入栈和出栈表示 与函数调用相关的 数据的入栈和出 栈,“前”表示返回地 址为else语句中“+” 运算符之前, “后” 表示返回地址为语 句中“+”运算符之后。
fibonacci(4) main()
执行 fibonacci(1), 输出形参值1
23
输出:
4321
因为实参值为1 (<2),所 以,执行
fibonacci(1)的if 部分,因而 fibonacci(1)返
回,fibonacci(1) 的相关数据出 栈;并将值1返 回到fibonacci(2) 的else部分(“+”
之后)
出现在 fibonacci(2) 中else部分 “+”之后的
返回值
1 fibonacci(2),前 1 fibonacci(3),前
fibonacci(4), main()
26
输出: 43210
因为fibonacci(0)返 回到fibonacci(2)的 else部分(“+”之
后),所以 fibonacci(2)返回, fibonacci(2)的相关
fibonacci(3),前 fibonacci(4) main()
执行 fibonacci(3), 输出形参值3
21
输出:
432
因为实参值为 3(>2),所
以,执行 fibonacci(3)的 else部分,因
而调用 fibonacci(2), fibonacci(2)的 相关数据入栈
执行 fibonacci(2), 输出形参值2
fibonacci(0),后 1 fibonacci(2),前
fibonacci(3),前 fibonacci(4) main()
执行 fibonacci(0), 输出形参值0
25
输出:
43210
因为实参值为0 (<2),执行
fibonacci(0)的if 部分,因而 fibonacci(0)返
回,fibonacci(0) 的相关数据出 栈;并将值1返 回到fibonacci(2) 的else部分(“+”
数据出栈;并将值 1+1=2返回到
fibonacci(3)的else 部分(“+”之前)
2 fibonacci(3),前 fibonacci(4), main()
27
输出: 432101
fibonacci(2)返回 到fibonacci(3)的 else部分(“+”之 前),因而继续
调用 fibonacci(1), fibonacci(1)的相 关数据入栈
递归程序设计的关键
如果要解决的问题、要计算的函数是以 递归方式定义的,相应的递归程序比较 容易设计
对于未曾给出递归定义、用单纯的循环 又难以表达清楚算法的较难问题,其关 键在于分析出问题的递归性所在
12
3、汉诺塔(梵塔)问题
传说在19世纪末,Bramah神庙的教士玩一种称为梵 塔(Tower of Hanoi)的游戏:共有三个木柱和n个 大小各异、能套进木柱的盘。盘按由小到大的顺序 标上号码1~n。开始时n个盘全套在A柱上,且小的 放在大的上面。
返回 4 + Summation(3) = 4 + 6 = 10
n 4
返回 3 + Summation(2) = 3 + 3 = 6
调用 2:
n
Summation(3) 3
调用 3:
Summation(2)
返回 2 + Summation(1)
=2+1= 3
n 2
n==1
返回 1
调用 4:
n
Summation(1) 1 11
fibonacci(1),后 2 fibonacci(3),前
fibonacci(4) main()
执行 fibonacci(1), 输出形参值1
28
输出: 432101
因为实参值为1
(<2),所
以,执行
fibonacci(1)的if
部分,因而
fibonacci(1)返
回,fibonacci(1) 的相关数据出
fibonacci(2),前 fibonacci(3),前
fibonacci(4) main()
22
输出:
4321
因为实参值为 2,所以,执 行fibonacci(2) 的else部分,
因而调用 fibonacci(1), fibonacci(1)的 相关数据入栈
fibonacci(1),前 fibonacci(2),前 fibonacci(3),前
通常系统会使用堆栈来保存与函数调用相关的 数据,当函数被调用时,相关数据入栈,函数 返回时,相关数据出栈
程序运行时,每次调用递归函数factorial()会在 内存中建立一个调用实例,调用结束时,此实 例从内存中消失
17
fibonacci函数的执行分析(续)
// 函数: fibonacci
C++语言中用递归函数来实现递归 递归函数(recursive function)
在其定义中直接或间接调用自己的函数 直接(direct)递归函数 间接(indirect)递归函数
4
1、递归程序设计概述(续)
递归程序设计的基本思想:
求解问题时通常将复杂的问题分解成若干子问题, 如果分解得到的某些子问题的解决方法与原来问题 的解决方法相同或相似,就可以使用递归技术
// 前置条件:n被赋值 && n > 0
// 后置条件:函数值==1到n的和
{ if ( n == 1)
// 基础分支
return 1 ;
else
// 递归分支
return ( n + Summation ( n - 1 ) ) ;
}
10
Summation(4) 的执行过程
调用1:
Summation(4)
19
输出: 4
调用 fibonacci(4)
, fibonacci(4) 的相关数据
入栈
fibonacci(4) main()
执行 fibonacci(4), 输出形参值4
20
输出:
43
因为实参值为 4(>2),所
以,执行 fibonacci(4)的 else部分,因
而调用 fibonacci(3), fibonacci(3)的 相关数据入栈
// 功能:使用递归方法计算Fibonacci序列。
int fibonacci(int n)
{
int result;
cout<<n<<“ “;
//为了更清楚的说明执行情况, //在函数中增加输出形参值的语句,
if (n < 2) result = 1; //基础分支
else result = fibonacci(n - 1) + fibonacci(n - 2); //递归分支
7
2、简单递归程序
[例]:编制函数计算Fibonacci序列
1 n=0 f(n) = 1 n = 1
f(n - 1) + f(n - 2) n > 1
分析: •这是一个典型的数学递归定义,可用递归程序解决。 •递归调用的基础分支是n<2
8
计算Fibonacci序列的递归函数
// 函数: fibonacci // 功能:使用递归方法计算Fibonacci序列。 int fibonacci(int n) { int result;
2 fibonacci(3),前 1
栈;并将值1返
fibonacci(4),
回到fibonacci(3) 的else部分(“+”
main()
之后)
29
输出: 432101
fibonacci(1)返回
到fibonacci(3)的
变量或常量是表达式 (表达式)是表达式 表达式 二元运算符 表达式 是表达式 … 这就用到了递归技术
许多由计算机解决的问题都要用到递归技术 求解问题时通常将复杂的问题分解成几个子问题,
如果分解得到的某些子问题的解决方法与原来问题 的解决方法相同或相似时,就可以用到递归技术。
3
1、递归程序设计概述(续)
} else { // 将disk_num - 1个盘从from柱移到aux柱,借助于to柱 move_tower(disk_num - 1, from, aux, to); // 将最下的盘从from柱移到to柱 cout << "Move disk" << disk_num << " from " << from << " to " << to << "\n"; // 将disk_num - 1个盘从aux柱移到to柱,借助于from柱 move_tower(disk_num - 1, aux, to, from);
其中1和3的解决方法与问题本身的解决方法相同,只 是盘数和柱子不同,因此可使用递归技术
注意:在使用递归技术时,多数情况下是解决问题的 方法相同,而处理的数据不同,这时就可将那些不同 的数据作为递归函数的参数。
14
解决汉诺塔问题的源代码:
// 函数: move_tower // 功能: 将disk_num个盘从from柱移到to柱,可以借助aux柱 void move_tower(int disk_num, char from, char to, char aux) {
游戏规则:
•将所有的盘从A柱移到C 柱,在移动过程中可以借 助另一个B柱 •每次只能移动柱最上面 的一个盘 •任何盘都不得放在比它 小的盘上
13
分析
汉诺塔问题看似复杂,但仔细分析一下,要将n个盘 从A柱移到C柱,可将此问题分解为三个子问题:
1. 将n-1个盘从A柱移到B柱 2. 将第n个盘从A柱移到C柱 3. 将n-1个盘从B柱移到C柱
递归函数的一般形式:
if (最易解决的情况) // 基础分支
解决语句
else
// 递归分支
递归函数调用
6
1、递归程序设计概述(续)
递归程序的可终止性:
正确的递归程序必须是可终止的 递归算法的基础分支通常就是终止分支 所有递归程序都必须至少拥有一个基础分
支,而且必须确保它们最终会达到某个基础 分支;否则,程序将永远运行下去,直到程 序缺少内存或者栈空间
每次递归使得问题规模变小,变得更容易解决 每个递归算法必须有一个基础分支和一个递归分支
基础分支(基线条件,base case):最容易解决的
情况,递归程序的最底层位置。在此位置时没有必 要再进行操作,可以直接返回一个结果。 递归分支(general case):普遍情况
5
1、递归程序设计概述(续)
if (n < 2) result = 1; //基础分支 else result = fibonacci(n - 1) + fibonacci(n - 2); //递归分支 return result; }
9
[例]:求1到n之间整数的和
int Summation (int n )
// 用递归方式计算1到n的和:将1到(n-1)的和加上n
第四章 递归程序设计
参见教材4.9节
1
主要内容及要求
主要内容
递归程序设计概述 简单递归程序 汉诺塔问题 递归程序的内部调用栈实现方式
要求
重点掌握递归程序设计的基本方法,以及递归程序 的执行过程
了解递归程序的内部调用栈实现方式
2
1、递归程序设计概述
递归是计算机科学中很重要的技术,例如,表达式 可以非形式化地定义为:
} return; }
15
ห้องสมุดไป่ตู้
move_tower函数的调用:
int main() {
// 将4个盘从A柱移到C柱,移动时利用B柱 move_tower(4, 'A', 'C', 'B'); return 0; }
16
4、递归程序的内部调用栈实现方 式 :fibonacci函数的执行分析
调用函数时,系统会分配一块内存保存与函数 调用相关的数据,包括:函数的返回地址、形 式参数、局部变量和返回值等
之前)
出现在 fibonacci(2) 中else部分 “+”之前的
返回值
1 fibonacci(2),前 fibonacci(3),前
fibonacci(4) main()
24
输出:
43210
fibonacci(1)返回 到fibonacci(2)的 else部分(“+”之 前),因而继续
调用 fibonacci(0), fibonacci(0)的相 关数据入栈
return result;
}
void main()
{
cout<<fibonacci(4);
}
18
首先main函 数的相关数
据入栈
main()
注:这是简化的堆 栈示意图,用函数 的入栈和出栈表示 与函数调用相关的 数据的入栈和出 栈,“前”表示返回地 址为else语句中“+” 运算符之前, “后” 表示返回地址为语 句中“+”运算符之后。
fibonacci(4) main()
执行 fibonacci(1), 输出形参值1
23
输出:
4321
因为实参值为1 (<2),所 以,执行
fibonacci(1)的if 部分,因而 fibonacci(1)返
回,fibonacci(1) 的相关数据出 栈;并将值1返 回到fibonacci(2) 的else部分(“+”
之后)
出现在 fibonacci(2) 中else部分 “+”之后的
返回值
1 fibonacci(2),前 1 fibonacci(3),前
fibonacci(4), main()
26
输出: 43210
因为fibonacci(0)返 回到fibonacci(2)的 else部分(“+”之
后),所以 fibonacci(2)返回, fibonacci(2)的相关
fibonacci(3),前 fibonacci(4) main()
执行 fibonacci(3), 输出形参值3
21
输出:
432
因为实参值为 3(>2),所
以,执行 fibonacci(3)的 else部分,因
而调用 fibonacci(2), fibonacci(2)的 相关数据入栈
执行 fibonacci(2), 输出形参值2
fibonacci(0),后 1 fibonacci(2),前
fibonacci(3),前 fibonacci(4) main()
执行 fibonacci(0), 输出形参值0
25
输出:
43210
因为实参值为0 (<2),执行
fibonacci(0)的if 部分,因而 fibonacci(0)返
回,fibonacci(0) 的相关数据出 栈;并将值1返 回到fibonacci(2) 的else部分(“+”
数据出栈;并将值 1+1=2返回到
fibonacci(3)的else 部分(“+”之前)
2 fibonacci(3),前 fibonacci(4), main()
27
输出: 432101
fibonacci(2)返回 到fibonacci(3)的 else部分(“+”之 前),因而继续
调用 fibonacci(1), fibonacci(1)的相 关数据入栈
递归程序设计的关键
如果要解决的问题、要计算的函数是以 递归方式定义的,相应的递归程序比较 容易设计
对于未曾给出递归定义、用单纯的循环 又难以表达清楚算法的较难问题,其关 键在于分析出问题的递归性所在
12
3、汉诺塔(梵塔)问题
传说在19世纪末,Bramah神庙的教士玩一种称为梵 塔(Tower of Hanoi)的游戏:共有三个木柱和n个 大小各异、能套进木柱的盘。盘按由小到大的顺序 标上号码1~n。开始时n个盘全套在A柱上,且小的 放在大的上面。
返回 4 + Summation(3) = 4 + 6 = 10
n 4
返回 3 + Summation(2) = 3 + 3 = 6
调用 2:
n
Summation(3) 3
调用 3:
Summation(2)
返回 2 + Summation(1)
=2+1= 3
n 2
n==1
返回 1
调用 4:
n
Summation(1) 1 11
fibonacci(1),后 2 fibonacci(3),前
fibonacci(4) main()
执行 fibonacci(1), 输出形参值1
28
输出: 432101
因为实参值为1
(<2),所
以,执行
fibonacci(1)的if
部分,因而
fibonacci(1)返
回,fibonacci(1) 的相关数据出
fibonacci(2),前 fibonacci(3),前
fibonacci(4) main()
22
输出:
4321
因为实参值为 2,所以,执 行fibonacci(2) 的else部分,
因而调用 fibonacci(1), fibonacci(1)的 相关数据入栈
fibonacci(1),前 fibonacci(2),前 fibonacci(3),前
通常系统会使用堆栈来保存与函数调用相关的 数据,当函数被调用时,相关数据入栈,函数 返回时,相关数据出栈
程序运行时,每次调用递归函数factorial()会在 内存中建立一个调用实例,调用结束时,此实 例从内存中消失
17
fibonacci函数的执行分析(续)
// 函数: fibonacci
C++语言中用递归函数来实现递归 递归函数(recursive function)
在其定义中直接或间接调用自己的函数 直接(direct)递归函数 间接(indirect)递归函数
4
1、递归程序设计概述(续)
递归程序设计的基本思想:
求解问题时通常将复杂的问题分解成若干子问题, 如果分解得到的某些子问题的解决方法与原来问题 的解决方法相同或相似,就可以使用递归技术
// 前置条件:n被赋值 && n > 0
// 后置条件:函数值==1到n的和
{ if ( n == 1)
// 基础分支
return 1 ;
else
// 递归分支
return ( n + Summation ( n - 1 ) ) ;
}
10
Summation(4) 的执行过程
调用1:
Summation(4)
19
输出: 4
调用 fibonacci(4)
, fibonacci(4) 的相关数据
入栈
fibonacci(4) main()
执行 fibonacci(4), 输出形参值4
20
输出:
43
因为实参值为 4(>2),所
以,执行 fibonacci(4)的 else部分,因
而调用 fibonacci(3), fibonacci(3)的 相关数据入栈
// 功能:使用递归方法计算Fibonacci序列。
int fibonacci(int n)
{
int result;
cout<<n<<“ “;
//为了更清楚的说明执行情况, //在函数中增加输出形参值的语句,
if (n < 2) result = 1; //基础分支
else result = fibonacci(n - 1) + fibonacci(n - 2); //递归分支
7
2、简单递归程序
[例]:编制函数计算Fibonacci序列
1 n=0 f(n) = 1 n = 1
f(n - 1) + f(n - 2) n > 1
分析: •这是一个典型的数学递归定义,可用递归程序解决。 •递归调用的基础分支是n<2
8
计算Fibonacci序列的递归函数
// 函数: fibonacci // 功能:使用递归方法计算Fibonacci序列。 int fibonacci(int n) { int result;
2 fibonacci(3),前 1
栈;并将值1返
fibonacci(4),
回到fibonacci(3) 的else部分(“+”
main()
之后)
29
输出: 432101
fibonacci(1)返回
到fibonacci(3)的
变量或常量是表达式 (表达式)是表达式 表达式 二元运算符 表达式 是表达式 … 这就用到了递归技术
许多由计算机解决的问题都要用到递归技术 求解问题时通常将复杂的问题分解成几个子问题,
如果分解得到的某些子问题的解决方法与原来问题 的解决方法相同或相似时,就可以用到递归技术。
3
1、递归程序设计概述(续)