若一个对象部分的包括它自己,或用它自己给自己定义,则称这个对象是递归的,递归也可定义为,在一个过程中直接或间接的调用自己,则这个过程也是递归的。
当一个问题具有以下三个特征时,就可以用递归算法。
1)大问题能分解成若干个子问;
2)子问题或是一个定值(直接解),或是与大问题具有相同性质的问题仅仅是规模比大问题小,即被定义项在定义中应具有更小的尺度;
3)子问题在最小尺度上有直接解,即分解过程能最终能结束(递归有结束条件)。
举个列子,比方说求n!(n为自然数)。正常人为的求解可能是1x2x3x—(n-1)xn。但这个问题可以分为两种情况考虑:
(1)当n=0时,n!=1
(2)当n>0时,n!=nx(n-1)!
再对照上述的三个条件,n!分为一个子问题n(为一个定值),和另一个规模比n!小的问题(n-1)!满足1),2)两个条件。再者(n-1)!可以继续分:(n-1)!=(n-1)x(n-2)!=(n-1)x(n-2)x(n-3)!=—,—x2x1!。而1!就等于1,所以子问题在最小尺度上有直接解,满足条件3)。其递归函数如下所示:
int fact(int n){
if(n==0) /*递归结束条件*/
return 1;
else
return (n*fact(n-1));
}
那么就产生问题呢,调用的函数在调用被调函数后是如何返回到调用点的下条语句?如何知道返回地址?下面来具体分析一下:当一个函数在运行期间调用另一个函数时,在运行被调函数之前应该完成三件事:
1)将所有的实参,返回地址的信息传递给被调函数保存;
2)为被调函数分配存储区;
3)将控制转移到被调函数的入口;
从被调用函数返回到被调用函数之前应该完成。然后接着完成以下三件事:
1.保存被调用函数的计算结果;
2.释放被调函数的数据区;
3.依照被调函数保存的返回地址将控制转移到调用函数。
多个函数嵌套的规则是,后调用先返回,此时内存的管理实行“栈式管理”,递归过程指向过程中占用的数据区,称之为递归工作栈,每一层递归参数合成一个记录,称之为“递归工作记录”。栈顶记录指示当前层的执行情况,称之为“当前活动记录”。栈顶指针,称之为“当前环境指针”
求解fact(4)的过程:
提到递归问题就不得不提一个很经典的问题—-汉若塔,汉若塔的背景我就不凑字数仔细描述了(qaq),其实就是将一摞盘子按半径从小到大的顺序,借助一座辅助塔B,从A塔搬到C塔。具体情况如下图:
其递归算法为:
void hanoi(int n, char X, char Y, char Z){
//将塔座X按直径由小到大且至上而下编号为1至的n个圆盘按规则搬到塔座Z上,Y可做辅助塔
if (n == 1){
move(X, 1, Z); //将编号为1的圆盘从X移动到Z
}
else
{
hanoi(n - 1, X, Z, Y);//将X上的编号为1至n-1的圆盘移动到Y,Z作辅助塔
move(X, n, Z); //将编号为n的圆盘从X移动到Z
hanoi(n - 1, Y, X, Z);//将Y上的编号为1至n-1的圆盘移动到Z,X作辅助塔
}
}
hanoi函数递归调用的过程:
具体代码实现(这里就用C++了):
#include
using namespace std;
void move(char X, int n, char Z){
cout << "正在将编号为:" << n << " 的圆盘从 " << X << " 圆柱移动到 " << Z << " 圆柱上" << endl;
cout << "\n";
}
void hanoi(int n, char X, char Y, char Z){
if (n == 1){
move(X, 1, Z);
}
else
{
hanoi(n - 1, X, Z, Y);
move(X, n, Z);
hanoi(n - 1, Y, X, Z);
}
}
int main(){
int n;
char X, Y, Z;
X = 65;
Y = 66;
Z = 67;
cout << "请输入圆盘总数: " << endl;
cin >> n;
hanoi(n, X, Y, Z);
return 0;
}