#Z0324. 函数与递归

函数与递归

No testdata at current.

在前面的习题里,我们看到了这样的程序:

#include <iostream>
using namespace std;
int fun1(int n) {
	if (n <= 1) {
		return 1;
	}
	return n;
}
int fun2(int n) {
	if (n <= 1) {
		return 1;
	}
	return n * fun1(n - 1);
}
int fun3(int n) {
	if (n <= 1) {
		return 1;
	}
	return n * fun2(n - 1);
}
int fun4(int n) {
	if (n <= 1) {
		return 1;
	}
	return n * fun3(n - 1);
}
int main() {
	int n;
	cin >> n;
	cout << fun4(n) << endl;
	return 0;

}

借助这个程序,我们可以计算 0 到 5 之间每个数的阶乘。如果要计算任意正整数 n 的阶乘呢?


很显然,我们不能定义无限多个函数,不然这个程序就要“长度爆炸”了。

int fun1(int n) {
	if (n <= 1) {
		return 1;
	}
	return n;
}
int fun2(int n) {
	if (n <= 1) {
		return 1;
	}
	return n * fun1(n - 1);
}
int fun3(int n) {
	if (n <= 1) {
		return 1;
	}
	return n * fun2(n - 1);
}
...


那么,如果我们通过如下的方式:

int fun(int n) { 
    if (n <= 1) { 
        return 1; 
    } 
    return n * fun(n - 1); 
} 

是不是就可以了?在这个函数中,就用到了函数的“递归调用”。什么叫递归调用呢?


所谓函数的递归调用,是指一个函数调用自身从而产生循环的过程。例如,对于刚才的程序:

int fun(int n) {
    if (n <= 1) { 
        return 1; 
    } 
    return n * fun(n - 1); 
} 

我们把fun(5)的递归过程描述出来:

002.jpg


也许你会有这样的疑问:为什么我要在fun函数里的一开始加上那个条件分支呢?下面这个程序有什么问题呢?

int fun(int n) { 
    return n * fun(n - 1); 
} 

程序调用fun(3)以后,会执行如下的过程:

调用fun(3)

计算3 * fun(2),调用fun(2)

计算2 * fun(1),调用fun(1)

计算1 * fun(0),调用fun(0)

计算0 * fun(-1),调用fun(-1) ......

整个函数就会无限地反复调用下去了。我们将递归过程中用来结束递归的条件分支称为递归的边界条件。

在递归函数的实现里,边界条件是非常重要的,如果没有正确的写出边界条件,可能会导致程序永远地执行下去。


递归与作用域

在一个带有递归过程的函数中,如果定义了变量,那么这个变量在递归的过程中,作用域会是怎样的呢?

int fun(int n) {
    if (n == 1) { 
        return 1; 
    } 
    int a = 0; 
    a++; 
    return 2 * fun(n - 1); 
}

当我们调用fun(3)时,会经过如下的过程:

003.jpg

在fun(3)里定义的a,作用域为红色框;在fun(2)里定义的a,作用域为蓝色框;在fun(1)里定义的a,作用域为绿色框。它们之间互不干扰,因此,既不会出现变量名冲突的情况,也不会出现同一个变量a的值被反复累加的情况。