递归知多少,我知是很少,哈哈哈
定义:
递归(recursion),又叫递推、迭代,就是当发生自身直接或间接调用本身的情况。
基本条件:
1结束条件(basecase):递归的出口
2不断前进(making progress):递归结束的趋向
对于,一个递归,是否可以使用的,不考虑效率、性能等问题,满足如上两个基本条件就可以了。
递归的调用过程:
一句话:“递归可以说是函数本身的嵌套调用!”
每次调用本身,都与调用其他函数差不多,都要经过在栈空间内的:初始化、指令处理、恢复现场三步,虽说是同意个函数,但是,每次开辟不同的空间,下面是一个基本演示,通过这个例子,你应该可以看出递归的调用过程,求n的阶层,代码如下:
/*
* 这个程序用来模拟递归的函数的调用过程
* 通过这个函数的输出,你可以看到,其实
* 递归在每次处理都经历了:初始化、处理
* 、恢复现场的操作,既浪费CPU,而且,对
* 于同一个参数n,每次都分配不同的内存,
* 浪费了栈空间内存。
*/
#include <iostream>
using namespace std;
int nnn(int n); //求n的阶层,即n!=1*2*3* … *n;
int m = 0; //控制输出空格,用来模拟调用
int main() {
cout<<nnn(4)<<endl;
return 0;
}
int nnn(int n) {
int result = 0;
//模拟入栈
m++;
for(int i = 0; i < m; i++) {
cout<<“/t”;
}
cout<<hex<<&n<<“: “<<dec<<n<<“, result = “<<result<<endl;
//
if( 0 == n) {
result = 1;
}else {
result = nnn(n – 1) * n;
}
//模拟出栈
m–;
for(int j = 0; j < m; j++) {
cout<<“/t”;
}
cout<<hex<<&n<<“: “<<dec<<n<<“, result = “<<result<<endl;
//
return result;
}
看到控制台的程序结果,你就很清晰了吧!
递归的使用;
使用情况:
如果你遇到的问题,可以划分,并且子问题的解决办法或性质与问题本身相同,除了规模等不同,我们考虑使用递归。
设计方法:
1问题划分
2确定算法
3设计出满足如上两个基本条件的递归程序
下面是一个递归的经典问题,汉诺塔问题,代码及问题描述如下:
/*
*递归中的经典问题:问题只是规模不同,处理方法是相同的,我们
*可以使用同样的方法处理。
*
*问题描述:有A,B,C三根柱子,其中A柱子上有n个盘子,怎样将n个盘子移动到C,保持顺序不变。
*
*分析: 如果是一个盘子,直接移到C就可以了,如果是2个,则要先将上面的那个移到B,再
* 将下面的移动到C,在将B那个移动到C,如果是n个呢,我们必须现将n-1个从A移到B,
* 在将最下面的一个移到C,再将B上的n-1个移动到C.
*/
# include<iostream>
using namespace std;
void move(int n, char a, char b); //其中n代表的是盘子的编号,a,b代表的是柱子的编号
void hanoi(int n, char a, char b, char c); //这里的n代表的是盘子数, a代表源头柱、c代表的是目的柱
int main() {
hanoi(3, ‘A’, ‘B’, ‘C’);
return 0;
}
void move(int n, char a, char b) {
cout<<“第 “<<n<<” 个:”<<“从:”<<a<<” 移到:”<<b<<endl;
}
void hanoi(int n, char a, char b, char c) {
if(1 == n) {
move(1, a, c);
}else {
hanoi(n-1, a, c, b);
move(n, a, c);
hanoi(n-1, b, a, c);
}
}
递归的性能:
递归的性能由于其调用过程,很是不好!所以,一般情况下,我们进行数值计算时不建议使用递归,上面求n的阶层完全可以使用循环代替。