深入理解递归
这学期学数据结构,这个女老师很要命,按同学的说法是常常自己把自己推翻了。讲课从来不按常理出牌,按老湿的说法是,4个2把俩王代出去了,一上来就是高潮,没有一点点逻辑……晕死…… 还好,自己还有一点自学能力,不然就真给她给悲催了。
闲话休提,言归正传。
这几天,看数据结构的二叉树部分,发现大多数的算法和定义涉及到了递归。特别是遍历,查找,求高度。刚开始看着部分就坐飞机也就是因为这个原因。今天恰好又突然想起了以前遇到的一个经典的关于递归的面试题。所以好好研究了一下函数递归的过程。
题目是:给定一个0~9到的自然数n,编写函数print()输出0~n的各个数字和其逆序。如: 给定n为 9,输出为: 0 1 2 3 4 5 6 7 8 9 8 7 6 5 4 3 2 1 0。
要求:不许用,循环,分支,判断等语句。print()参数自定。函数内只允许有一个语句。
当时看到这题,觉得这货很变态。不是坑爹嘛,不用循环,分支,判断就算了,还只让有一个语句。
无赖中,只有去CSDN请教高手。CSDN果然是名不虚传,高手云集。帖子发上一会儿,各种招数套路纷纷都上来了。高手就是高手啊。虽然解法众多,但最后大家一致肯定的答案是下面这个:
1 #include<iostream>
2 using namespace std;
3
4 int print(int a,int b)
5 {
6 return cout<<a && (a >= b || print(a + 1, b)) && (a >= b ||(cout<<a));
7 }
8
9 int main()
10 {
11 int a=0,b=9;
12 print(a,b);
13 return 0;
14 }
函数只用了一个 return 语句。其中用到了逻辑运算和函数递归。可是高手们有个缺点就是,只给你答案,从不说这是为神马。所以,最后还是得自己去研究。
在分析这段代码前,我们先看看一些简单的递归例子,以便于理解。
求阶乘:
1 #include<iostream>
2 using namespace std;
3
4 int fun(int n)
5 {
6 if(n==1||n==0)
7 return 1;
8 else
9 return n*fun(n-1);
10 }
11 int main()
12 {
13 cout<<fun(3)<<endl;
14 return 0;
15 }
这段代码,学过点C的人肯定都看的懂了。老谭的书上174页有详解。用通俗的说法来讲,fun函数自身的代码用到了自己。这是不是很滑稽,确实,如果没有第6,7行的关键语句,这个代码就真的很无聊了。因为,它会一直运行,调用自己->调用自己…………直到程序的栈存储区溢出崩溃,你可以试一下这个代码
1 void fun(int n)
2 {
3 fun(n-1);
4 }
5
6 int main()
7 {
8 fun(3);
9 return 0;
10 }
是不是崩溃了!(如果没有,肯定是你的电脑超神了,可以不再看下去了……)回到正题。为了每一次递归调用的现场保护,程序都会将每个中断的现场进行入栈保护,等到递归条件满足返回时,再一个个退栈,恢复现场。为什么要这样做了?其实可以这样理解:
甲,乙,丙,丁四个打入敌人内部的地下党,分别潜伏在不同的区域。甲要得到一个关于敌人城防部署的情报,所以他给已发了消息。由于所处敌区的位置不同,已并未得到这个情报,所以按照约定,已也给他的下一级并丙发出了同样的命令。就这样,消息一级级的传到了丁那里,丁立即做出反应,将情报传给上一级丙……几经周折,最终甲得到了敌人的城防部署,将敌人一网打尽。
这个例子只是很通俗的解释了一下。要理解还得看代码。我们要求n的阶乘,然而根据已知条件:
n=0,1时 n!=1;n>1时,n!=n*(n-1)!;
未完待续………………