4.6 函数的嵌套调用和递归调用
4.6.1 函数的嵌套调用
C++程序中允许函数的嵌套调用。所谓嵌套调用指的是在调用A函数的过程中,可以调用B函数;在调用B函数的过程中,还可以调用C函数。当C函数调用结束后,返回到B函数;当B函数调用结束后,再返回到A函数。前面讲过的例4.2就是函数嵌套调用的一个例子。下面写出该程序执行后的输出结果:
It is in main
It's in fun2.
It's in fun1.
It's in fun3.
It's back in fun1.
It's back in fun2.
It is back in main.
该结果给出函数嵌套调用的具体执行过程。下面以图示进一步说明,如图4-1所示。图中①、②……⑨表示嵌套调用的执行过程。
main() -> fun2() -> fun1() -> fun3()
① ② ③ ④
⑧ ⑦ ⑥ ⑤
嵌套调用是经常使用的。下面举一个函数嵌套调用的例子。
例4.16 编写程序求出下列式子的和:
编程如下:
#include <iostream.h>
const int k = 4;
const int n = 6;
int sum_of_powers(int k, int n);
int powers(int m, int n);
void main() {
cout << "Sum of " << k << " powers of integers from 1 to " << n << " = ";
cout << sum_of_powers(k, n) << endl;
}
int sum_of_powers(int k, int n) {
int sum = 0;
for (int i = 1; i <= n; i++)
sum += powers(i, k);
return sum;
}
int powers(int m, int n) {
int product = 1;
for (int i = 1; i <= n; i++)
product *= m;
return product;
}
该程序执行后,输出结果如下:
Sum of 4 powers of integers from 1 to 6 = 2275
在 main()
中调用了 sum_of_powers()
函数,而在 sum_of_powers()
函数中又调用了 powers()
函数,这就是函数调用的嵌套。
4.6.2 函数的递归调用
在C++语言的编程中,允许使用函数的递归调用。所谓函数的递归调用是指在调用一个函数的过程中出现直接或间接调用该函数自身的情况。例如,在调用 f1()
函数的过程中,又调用了 f1()
函数,这称为直接递归调用;而在调用 f1()
函数的过程中,调用了 f2()
函数,又在调用 f2()
函数的过程中调用了 f1()
函数,这称为间接递归调用。
1. 递归调用的特点
在实际问题中,有许多问题可以采用递归调用的方法来解决。下面举一个求正整数5的阶乘问题,即5!。由于5!可以化为5×4!, 4!又可以化为4×3!, 3!可以化为3×2!, 2!可以化为2×1!, 最后1!化为1×0!, 这时,0!等于1是已知的。于是可求出5!为5×4×3×2×1×1等于120。这就是一个既简单又典型的递归调用问题的例子。可见,使用递归调用解决问题的方法如下:
原有的问题能够分解为一个新的问题,而新的问题又用到了原有问题的解法,这就出现了递归。按照这一原则分解下去,每次出现的新问题是原问题简化后的子问题,而最终分解出来的新问题是一个已知解的问题。这便是有限的递归调用。只有有限的递归调用才是有意义的,无限的递归调用在实际中没有意义。
使用递归调用方法编写的程序简洁清晰,可读性强。因此,递归是算法分析与程序设计的一个重要内容。对于能够用递归方法编程的问题,要尽量使用递归。但是,用递归方法编写的程序执行起来在时间和空间的开销上比较大,既要花费较长的计算时间,又要占用较多的内存单元,因为递归的过程中要占用较多的内存单元存放“递推”的中间结果。因此,在速度慢、内存小的机器上难以使用递归,只好用迭代的方法编程。现在,机器的速度高、内存大,一般来说,在支持递归算法的语言中应尽量使用递归。
2. 递归调用的过程
递归调用的过程可分为如下两个阶段。
(1) “递推”阶段
将原问题不断分解为新的子问题,逐渐从未知向已知的方向推测,最终到达已知的条件,即递归结束条件,这时递推阶段结束。
3! = 3 * 2!
2! = 2 * 1!
1! = 1 * 0!
0! = 1 (已知条件)
(2) “回归”阶段
该阶段是从已知的条件出发,按照“递推”的逆过程,逐一求值回归,最后到达递推的开始处,结束回归阶段,完成递归调用。
0! = 1 (已知条件)
1! = 1 * 0! = 1
2! = 2 * 1! = 2
3! = 3 * 2! = 6
下面以求3!为例,写出递归调用的两个阶段,展示全过程,如图4-2所示。
例4.17 编程计算某个正整数的阶乘。假定从键盘上输入一个数存入变量n中,求n!。程序内容如下:
#include <iostream.h>
long int fac(int n);
void main() {
int n;
cout << "Input a positive integer: ";
cin >> n;
long fa = fac(n);
cout << n << "!=" << fa << endl;
}
long int fac(int n) {
long int p;
if (n == 0)
p = 1;
else
p = n * fac(n - 1);
return p;
}
执行该程序,显示如下提示:
Input a positive integer: 8
结果为:8! = 40320
其递归过程前面已经分析过了。
例4.18 编程求出Fibonacci数列的第n项。Fibonacci数列定义如下:
假定求出第8项。
编程如下:
#include <iostream.h>
const int N = 8;
long Fibo(int n);
void main() {
long f = Fibo(N);
cout << f << endl;
}
long Fibo(int n) {
if (n == 1)
return 0;
else if (n == 2)
return 1;
else
return Fibo(n - 1) + Fibo(n - 2);
}
该程序执行后输出结果如下:
21
请读者自己分析该程序中递归调用的执行过程。