通篇隐喻,看官自明。
《目录》
- 递推的感性认识
- 递归的感性认识
- 八皇后摆放问题
- 蹭同桌饭的游戏
- 为你写诗 --递归
- 练习·为你(递归)写诗
- 尾递归与不爆栈的递归
开元盛世
开元二十三年(735年)的长安,唐玄宗亲临五凤楼,恩赐百姓宴饮狂欢,还让三百里之内的地方官带歌舞团进京,在楼前表演竞技。
行路难,归去难!
就在这一年,二十四岁的杜甫考场失利。身上的盘缠抵不上长安的物价,也没有亲友可以长久依靠。于是,如同现在去一线城市打拼却拼不出一丝光明的年轻人一样。把身上仅有的盘缠去旅行......
走到山东时,看见高耸的泰山。那雄浑的景色让ta心旌摇荡 (面对自己想要或想得到的东西,心驰神往,不能自制),还处在山脚时便被山势激起豪情而写下了《望岳》。
会当凌绝顶,一览众山小。
与生活中平常视角极其不同的是:登上泰山,眼前的一切都便的微不足道了。不仅激发杜甫的热血,还有、还有ta告诉我们,种种碎屑的苦闷只是因为站的太低、看得太近...
那么,有没有打开眼界的方法呢 ... ...
人,人天然的思维方式叫正向思维,而人的正向思维被称为 "递推"(Iterative)。递推是人本能的正向思维,我们小时候学习数数,从1、2、3 ··· 100 ··· 10000 ··· ∞,就是典型的递推;这种学习过程是循序渐进,由易到难,由小到大,由局部到整体,出发点是正向的;
其实人对数字没什么概念,数字多寡只是一个名称,对大数基本是傻的,甚至对小的数也不敏感,习惯从小到大渐渐扩展的思维。
山水之乐,得之心而寓之酒也。
小学时,学习解方程,学习解一元方程(一个未知数x);
中学时,学习解方程,学习解二元(俩个未知数x,y)、三元方程(三个未知数x,y,z);
大学时,学习解方程,学习解任意未知数方程(线性方程组)、齐次方程、微分方程;
类似于,学习语言时,先从拼音开始,再组词,后简单造句,接着使用"因为...所以..."、"既然...于是..."、"不但...而且" 等连接词,造成稍稍复杂的句子,这种思维方式便是递推,ta是正向的。
如果用递推的方式计算一个整数的阶乘, 7! = 1 * 2 * 3 * 4 * 5 * 6 * 7。在生活里,我们并不觉得ta不合理因为这是天然而成的;
中学的数学归纳法就是递推方式的典型应用,在高中的数列里面是最常用。
资料:数学归纳法
但世界上所有问题,不是一种角度就可以解决,例如,19世纪国际象棋棋手贝瑟尔提出一个问题,在 8 * 8 格但国际象棋上摆放 8 个皇后,使其不会相互攻击(任意俩个皇后不能在同一列、行、斜线),问有多少种摆放 ?
递推的思路 : 首先在第一行摆好一个棋子,它当然不会和任何棋子有冲突,然后再在第二行摆一个棋子,保证它和第一个棋子没有冲突,然后再摆第三行的,以此类推。下边给出了一种可能的解法。
数学王子---高斯穷其一生只找到了76种方案(其他人是20多种)。当然今天用计算机很快就能找到它的全部92种结果。如果您花几分钟,一定可以找到一种,而找到 ta 所有可能的摆法近乎不可能,我把原因告诉您。
人之所以做不到穷举所有合理的摆放方法,倒不完全是因为它的排列组合非常多。事实上,“八皇后”问题所有可能的组合只有4万种,并不算多,更何况在这4万种排列中,大量的情况是一眼看上去就知道不合理的,可以马上排除。因此,即使一个智力中庸的人手工处理4万种情况,照说有几个月也该搞清楚了,更何况是数学天才高斯。高斯没有做出来并不是因为他水平不够,实际上当时很多的国际象棋选手们都在解这道题,找到的答案只有20多种,还不如高斯呢。这里面主要的原因是人类固有的递推思维方式,也就是从前到后,从小到大,并不适合解决这道题。
那么问题该怎么解决?也许有 76 + 1 种呢 ?
数学家推算 8皇后问题解法共 92 种。以 12 种 8皇后 排列方法延伸(人要推出12种并不难),把11种中每一种每逆时针旋转 90 度,即可得到一种排列方法,旋转一周即可得到 4 种排列。得到这 4 种排列又按照右下左上的对角线对称翻身,又可得到 4 种方法,所以这里一共 11 * (4+4) = 88 种,而第十二个排列TA只能旋转90度2次,所以也只能翻身2次。算出来就是 88 + (2+2) = 92 种。
p.s. 这是一种解法,把这张图旋转90度,又可以得到另一种解法!
计算机,计算机的正向思维被称为 "递归"(recursive),计算机天生就是为大数处理而制造的,习惯从大到小、层层分解的思维。
计算机是怎么计算阶乘的呢?它是倒着来的。比如要算5!,计算机就把它变成5x4!(五乘以四的阶乘)。当然,你会说,4!还不知道呢。没关系,计算机会说,采用同样的方法,把它变成4x3!。至于3!,则用同样的算法处理。最后做到1!时,计算机知道了它就等于自己,即1!=1,从此不再往下扩展了。接下来,就是倒推回所有的结果,由于知道了1!,2!,然后3!,4!,5!就统统都知道了,ta只需要依靠一个栈的数据结构(先进后出,后进先出,艾伦·图灵1946年提出,解决子程序的调用和返回)。从上向下展开 5, 4, 3, 2, 1 得到一个具体的结果就从下往上 1,2,3,4,5,当栈里面的数字全部出栈,递归算法就结束了。
这样的算法逻辑非常简单。递归过程的每一步用的都是同一个算法,计算机只需要自顶向下不断重复而已。具体到阶乘的计算,无非就是某个数字N的阶乘,变成这个数乘以N-1的阶乘。因此,递归的本质有两条:其一是自顶而下,其二是自己不断重复。
用递归的方式解决八皇后问题,思路十分清晰:
【宏观】
1. 假如棋盘上已经有了 7 个皇后,它们彼此的位置不冲突,那么在第八行从第 1 个位置到第 8 个位置一个一个地试验即可。当然,你会问前七个皇后怎么摆,才能保证它们彼此不冲突呢?答案很简单,按照第八步的方法照猫画虎地做。
2. 当棋盘上一个棋子没有的时候,从第一行的第一个位置到第八个位置一个个分别试一试。只关心自己的下一层,而不是更下面的细节。
【微观】
1. 采用回溯法(backtracking)
2. 编写一个函数,把一个皇后放在某行的第 1 列,检查TA是否与棋盘上的其TA皇后相互攻击的局面,函数返回一个状态。
3. 如果皇后可放在这个位置,函数便调用自身,把皇后放在下一行。
4. 当递归调用返回时,函数再把原先那个皇后移到下一列。
5. 最后一个皇后成功置于最后一行时,函数打印棋盘,并显示 n 个皇后的位置
有的摆放往后可以走得通,有的走不通。如果走不通,计算机会直接跳到下一个位置去试验。实际上在4万个摆法中,有92个是能走通的。4 万个摆法对计算机连 1 秒都不需要。
#include
#include // 非标准头文件, 调用getch( ) 可以一起去掉,功能 : 按任意键继续的
#define Abs(x) ((x^(x>>31)) - (x>>31)) // 取绝对值
int iCount = 0; // 记录解的序号的全局变量
int Queens[8]; // 记录皇后在各列上的放置位置的全局数组
void Output( ) // 输出一个解,即一种没有冲突的放置方案
{
int i, j, flag = 1;
printf("第%2d 种方案(♥表示皇后):\n", ++iCount); // 输出序号。
printf(" ");
for( i = 1; i <= 8;i ++ )
printf("▁");
putchar(10);
for ( i = 0; i < 8; i ++ )
{
printf("▕");
for ( j = 0; j < 8; j ++ )
{
if( Queens[i]-1 == j )
printf("♥");
else
{
if ( flag < 0 )
printf(" ");
else
printf("█");
}
flag = -1 * flag;
}
printf("▏\n");
flag = -1 * flag;
}
printf(" ");
for( i = 1; i <= 8; i ++ )
printf("▔");
putchar(10);
getch(); // conio.h 调用getch( ) 可以一起去掉
}
int IsValid( int n ) // 判断第 n 个皇后是否与前面皇后行成攻击
{
int i;
for ( i = 0; i < n; i ++ ) // 第 n 个皇后与前面 n-1 个皇后比较
{
if ( Queens[i] == Queens[n] ) // 两个皇后在同一行上,返回 0
return 0;
if ( Abs(Queens[i] - Queens[n]) == (n - i) ) // 两个皇后在同一对角线上,返回 0
return 0;
}
return 1; // 没有冲突,返回 1
}
void Queen( int n ) // 在第 n 列放置皇后
{
int i;
if ( n == 8 ) // 若 8 个皇后已放置完成
{
Output(); // 输出
return;
}
for ( i = 1; i <= 8; i ++ ) // 在第 n 列的各个行上依次试探
{
Queens[n] = i; // 在该列的第 i 行上放置皇后
if ( IsValid(n) ) // 没有冲突,就开始下一列的试探
Queen(n + 1); // 递归调用进行下一步
}
}
int main( void )
{
printf("八皇后排列方案:\n");
Queen(0); // 从第0列开始递归试探
getch(); // 可以换成 system("pause")
return 0;
}
对于“八皇后”这种问题,只能在逻辑上和实现上,都采用递归的方法,很难采用递推,即使采用了递推,也不过是人工实现递归中的堆栈操作。
拆解问题的过程就是一个自顶向下的过程。
e.g. 上海有多少辆自行车这个问题,先拆解成自行车数量 = 个人自行车数量 + 公用自行车数量 + 共享单车数量。
个人自行车数量 = 人均个人自行车数量 * 总人数
公用自行车数量 = 公用自行车点的数量 * 每个点的自行车数量。
共享单车数量 = 共享单车平均密度 * 面积。
这种拆解方法就是从顶层开始拆解,这或许也是为什么谷歌等公司愿意将这类问题当面试题的原因。
计算机是人制造出来处理大场景的,对于大小场景也可以使用递推、递归切换使用。
递推: 一直不相信明天不能到来,所以,纵情做自己喜欢的事情,整个人生不会会坚定的目标,整个人生没有一件坚持到底的事情。去世时刻,回望一世倒是匆匆、静静。
递归: 如果把做事情这种倒推的方式扩展到极限,那就是按照生命的长度倒推自己这辈子该做的事情。懂得取舍,抓住主要因素。时时刻刻,纵心而为,这一刻恰恰迎来,静静的享受这一刻是多么满足和充实。苍老而疲惫的身体掩不住,明亮的眸子戳过恍惚迷茫的时间海洋,许多人在和我们 ??♂️ 挥手告别。
递归的食用指南:
递归的原理在计算机科学中更多地体现在逻辑上的做事方法,在这方面它没有太多的缺点。
在具体实现上,如果直接采用递归的方法,逻辑简单,代码很短,非常漂亮。但是,由于要不断地把中间状态放到堆栈中(成为压栈或者入栈),然后再出来(成为弹出栈或者出栈),占用空间很多。尾递归如果在得到编译器的帮助下,是完全可以避免爆栈的原因:每一个函数在调用下一个函数之前,都能做到先把当前自己占用的栈给先释放了,尾递归的调用链上可以做到只有一个函数在使用栈,因此可以无限地调用!这并不是所有语言都摆支持,有些语言,比如说 python, 尾递归的写法在 python 上就没有任何作用,该爆的时候还是会爆,C语言是支持的 ~
因此,在具体实现时,很多情况是使用递归的原则设计,递推的原则实现。比如真正做阶乘运算,并不需要从大往小计算,还是可以从小往大算的。思路相同,只是实现方案不一样。
另外,给出 Python 版本的八皇后
abs = lambda abs:-abs if abs < 0 else abs # 取绝对值函数
Queens=[0,0,0,0,0,0,0,0] # 记录皇后在各列上的放置位置的全局列表
iCount = 1 # 记录解的序号的全局变量
def Output( ): # 输出一个解,即一种没有冲突的放置方案
global iCount
flag = 1
print("第%2d 种方案(♥表示皇后): " % iCount)
iCount += 1
print(" ",end='')
for i in range(8):
print("▁",end='')
print(' ')
for i in range(8):
print("▕",end="")
for j in range(8):
if Queens[i]-1 == j:
print("♥",end="")
else:
if flag < 0 :
print(" ",end="")
else:
print("█",end="")
flag = -1 * flag
print("▏")
flag = -1 * flag
print(" ",end="")
for i in range(8):
print("▔",end="")
print("")
def IsValid( n ): # 判断第 n 个皇后是否与前面皇后行成攻击
global Queens
for i in range(n): # 第 n 个皇后与前面 n-1 个皇后比较
if ( Queens[i] == Queens[n] ): # 两个皇后在同一行上,返回 0
return 0;
if (abs(Queens[i] - Queens[n]) == (n - i)): # 两个皇后在同一对角线上,返回 0
return 0
return 1 # 没有冲突,返回 1
def Queen( n ): # 在第n列放置皇后
global Queens
if n == 8 : # 若 8 个皇后已放置完成
Output( ) # 输出
return
for i in range(1,9): # 在第 n 列的各个行上依次试探
Queens[n] = i # 在该列的第 i 行上放置皇后
if IsValid(n): # 没有冲突,就开始下一列的试探
Queen(n + 1) # 递归调用进行下一步
if __name__ == '__main__':
print("八皇后排列方案:\n")
Queen(0); # 从第0列开始递归试探
看电视剧有感:
坏人的智商比坏人自己生活的圈子是高的、做事果断,失败是因为有圈子外面的人来了,圈子外面的人也许更理性、更热情;
很少人天然就坏,但生活仿佛安排好了,"猪脚"自己又选择贪婪(局部最优),于是递推的走向了以前的反边。也许,要舍得才会跳出困境的极限,递归的走才能完成大事。 我们要比坏人强。至于怎么比他们强,不完全是比他们更努力,因为这只是在低层次上的竞争,而是首先在见识和格局上要比他们高,气度要更大,这句话就是告诉您多采用采用递归的思维方式。
行万里路和读万卷书是相辅相成的,千万不要只做其一。
平常生活中,行万里路可以把您摆脱工具意识。
因为在熟悉的场景里,您看到的一切或多或少都会工具化。
一个人如果常年生活在工具化的心态里,一定会面目可憎、一脸俗气,连他自己都觉得乏味。这时候,就有必要换到一个陌生的场景。工具化心态也会让人低迷,感到对人生的无望......
当我站在新的十字路口上,我并不知道每一条路通往何方,也并不急着赶路。这就意味着,这个路口对你来说并不是通往某个目标的工具。
这样的话,就算走错了路我也不会生气,只会用悠然的心态欣赏街景。步子快一点或慢一点都无妨,因为没有了目标,就不需要执行力;而不需要执行力,当然就不需要苛刻的时间表。
今天的很多人之所以把旅行搞得很疲倦,也不觉得有任何审美体验,就是因为不懂得这个道理,没有在时间和空间上让自己和环境拉开足够的审美距离。
摆脱工具意识,只需要简简单单的换一座普普通通的城市即可,还有、还有像《望岳》一样,从事物的极限看过来.....
我们来玩俩个游戏 “抢 30 游戏”:
人数:俩人
规则:俩人轮流说数字只能是 "1"、"2";选择1或2开始,报数字(1、2)循环叠加值,直到30结束,说到30的那个人胜!
递推思考: 从1,2,3开始找规律就难了。我们假定数到30有F(30)种不同的路径,那么到达20这个数字,前一步只有两个可能的情况,即从28直接蹦到30,或者从29数到30。
由于从28蹦到30,和29到30,彼此是不同的,因此走到30的路径数量,其实就是走到28的路径数量,加上走到29的路径数量,也就是说F(30)=F(28)+F(29)。类似地,F(29)=F(28)+F(27)。这就是递推公式。
最后,F(1)只有一个可能性,就是1,F(2)有两个可能性,要么直接蹦到2,要么从1走到2。知道了F(1)和F(2),就可以知道F(3),然后再倒着推导回去,一直到F(30)即可。
数学比较好的朋友,可能已经看出来这是著名的斐波那契数列,如果我们认为F(0)也等于1,那么这个数列是这样的,1(=F(0)),1,2,3,5,8,13,21,……这个数列几乎按照几何级数的速度增长,到了F(30),就已经是 832040 了。因此,靠穷举法是不可能把所有情况想清楚的因为人不是计算机呀。
// C语言实现到90位
#include
typedef long long int_64;
int main( int argc, const char *argv[] )
{
int_64 f1 = 1, f2 = 1;
for( int_64 i = 1; i <= 90; i ++ )
{
printf("%5d : %19ld\n%5d : %19ld\n", i++, f1, i, f2); // 不同编译器i++实现顺序不同,可能需要改动
f1 = f1 + f2; // 前两个月累计
f2 = f1 + f2; // 同上
}
return 0;
}
要想抢到30,就需要抢到27,因为抢到了27,无论对方是加1还是加2,您都可以加到30。而要想抢到27,就要抢到24,以此类推,就必须抢到21、18、15和12、9、6、3。因此按照这种规律,只要谁抢到了3,TA就赢定了。这里面最核心的地方在于看清楚,无论对方选择 1 还是 2,你都可以让这一轮两个人加起来的数值等于3,于是您就可以牢牢控制整个过程了。
代码实现:
#include
#include
#include
int input( int t );
int copu( int s );
int main( int argc, char *argv[] )
{
int tol = 0;
printf("\n* * * * * * * *catch thirty* * * * * * * \n");
printf("Game Begin\n");
rand(); /*初始化随机数发生器*/
/*取随机数决定机器和人谁先走第一步。若为1,则表示人先走第一步*/
if( rand()&1 ) // &1等同 % 2
tol=input(tol);
while(tol != 30) /*游戏结束条件*/
if( ( tol = copu(tol) ) == 30 ) /*计算机取一个数,若为30则机器胜利*/
printf("You lose! \n");
else
if( (tol=input(tol)) == 30 ) /*人取一个数,若为30则人胜利*/
printf("computer lose! \n");
printf(" * * * * * * * *Game Over * * * * * * * *\n");
return 0;
}
int input( int t )
{
int a;
do{
printf("Please count:");
scanf("%d", &a);
if(a>2 || a<1 || t+a>30)
printf("Error input,again!");
else
printf("You count:%d\n", t+a);
}while(a>2 || a<1 || t+a>30);
return t+a; /*返回当前已经取走的数的累加和*/
}
int copu( int s )
{
int c;
printf("Computer count:");
if((s+1)%3 == 0) /*若剩余的数的模为1,则取1*/
printf(" %d\n",++s);
else
if((s+2)%3 == 0)
{
s += 2; /*若剩余的数的模为2,则取2*/
printf(" %d\n",s);
}
else
{
c = (rand()&1)+1; /*否则随机取1或2, &1等同%2*/
s += c;
printf(" %d\n",s);
}
return s;
}
小游戏,“21根火材”。玩这个我同桌请了我 10 顿饭,唉(松了一口气),最后还是我故意输给了 TA。
人数:2人
规则:俩人轮流取火材,最少取 1 根,最多取 4 根,不可多取,也不可不取,谁取最后一根火材谁输。
蕴含规律:后取者稳赢。
代码实现 :
// 21火柴游戏 - C++
#include
using namespace std;
int main(int argc, char *argv[])
{
int num = 21, man, computer;
while( num != 1 )
{
cout<<"共有"<>man;
if( man > 4 ||man < 1 )
continue;
cout<<"\n计算机取"<<5-man<<"根火柴,还剩"<
从数学的角度来分析,为什么 后手稳赢 ?
21 根火材,21 = 4*(1+4)+1,我们可以利用 5 这个数字。
规则:俩人轮流取火材,最少取 1 根,最多取 4 根,不可多取,也不可不取,谁取最后一根火材谁输。
从最简单的情况考虑,玩这个火柴游戏,最少的火柴数至少得是 5 根,因为先手如果取最大数 4,后手至少还可以取 1 根。
假设我们是后手:
那 5 根火柴时,后手取的数 = 5 - 先手取的数 ,这样就稳赢。
现在换成 21 根火柴,道理相同:
构成 5 这个数字,双方轮流取 4 次(4 * 5 = 20) ,就只剩下 1 根火材了,而这时取的人是后手,因此后手稳赢。
某天,我被一个大学叫过去测试,其中有一个类似的题目。
这个题目是在赤裸裸的干啥我们用猫腻,我想了一会说 3 根,哈哈当然对了啊。
后来,我把这个问题拓展为 n 根火柴,双方轮流取,最少取 1 根,最多取 m 根,n 和 m 是随机的。
无论是 21 还是 15 根火柴,取胜策略就是 1 + m ,一直给对手留下 1 + m 的整数倍。
21 = 4*(1+4) + 1
15 = 3*(1+3) + 3
那 n 根火柴,双方轮流取,最少取 1 根,最多取 m 根,取到最后 1 根为胜,是不是可以写为:
n = t*(1+m) + r
若 r = 0 [如 20 = 4*(1+4)+0],火柴数: t*(1+m),先手无论取多少,都会被后手取为 1+m ,t 次后,后手一定会拿到最后一根。
若 r > 0 [如 21 = 4*(1+4)+1],火柴数: t*(1+m) + r,如果先取 r 个,局面就变成了 r =0,这样的局面先手才会能到最后一根火柴。
因此,无论 r = 0 或者 r > 0,只要一直给对方留下 1 + m 的整数倍,我们就会赢。
编程实现也很简单,这个程序只是简单的演示,并没有考虑安全性---至少函数的接收参数没有做边界检查。
// 人机博弈
#include
#include
#include
int m, n;
void Init( );
int flag( );
void Game( int who_first );
int main( void )
{
Init( );
int who_first = rand( ) % 2;
Game( who_first );
return 0;
}
void Init( void )
{
srand((unsigned int)time(NULL));
m = rand( ) % (5-2+1)+2; // 生成 [2, 5] 范围的随机数
n = rand( ) % (30-15+1)+15; // 生成 [15, 30] 范围的随机数
printf(" 共 %d 根火柴,每轮取 [1, %d] 根\n", n, m);
sleep(1.5);
printf(" 游戏规则:双方轮流取,每次至少取 1 个,至多取 %d\n", m);
}
void Game( who_first )
{
int i = 1, p1, p2;
while( n > 0 )
{
// 人取
if( who_first || i > 1 )
{
scanf("%d", &p1);
if( p1 == 0 || p1 > n || p1 > m ){
puts("\n不符合条件,请重取"); continue;
}
n -= p1;
printf(" 我:> 取 (%d) 根火柴,余 (%d) \n", p1, n);
if( n == 0 ) {
puts("⊙▽⊙ 博弈结束 ,您获胜了哟");
break;
}
}
// 计算机取
int r = n % (m+1);
if( !r )
p2 = m-1-rand( ) % (m-1);
else
p2 = r;
n -= p2;
printf(" AI:> 取 [%d] 根火柴,余 [%d]\n", p2, n);
if( !n ){
printf("唔,[AI] 赢了");
break;
}
i ++;
}
}
我想练习即写诗。带着对递推和递归的认识漫步于诗情画意的阳光下吟吟诗,如果词汇量较少,没关系我送您一个工具 是 爬虫实现的语意分析之相关词汇 ,帮您做文字的排列组合来演绎打动人心的诗篇。
最后把您做的诗分享在评论区吧。加油~
可递归的对象:
集合、树、图、多边形、串、等等。如图,删除图的任意一个结点,依然是图,只不过小了。
从前有座山,山里有座庙,庙里有个老和尚在讲故事讲的是,
从前有座山,山里有座庙,庙里有个老和尚在讲故事讲的是,
从前有座山,山里有个庙,庙里有个老和尚在讲故事讲的是,
从前......
从前......
这是一个说明递归性质的例子,也是尾部递归的典型例子。
其实ta表达的并不正确因为没有结束条件,这会造成死循环。
玩探险游戏时,有这样的一些房间,每个房间都有俩入口和出口。入口可以进入下一个房间,出口可以回到上一个房间。
现在我们从第一个房间出发,在房间中寻找藏宝图和勋章,如果在某个房间找到就原路返回。
火精灵:金木水火土,我藏在了第 4 个房间。
- 起点:第 1 个房间,寻找ing...
- 没有,进入第 2 个房间
- 第 2 个房间,寻找ing...
- 没有,进入第 3 个房间
- 第 3 个房间,寻找ing...
- 没有,进入第 4 个房间
- 第 4 个房间,寻找ing...
- 找着了带着,原路返回 ?
- 离开第 4 个房间
- 离开第 3 个房间
- 离开第 2 个房间
- 回到第 1 个房间
void search(int depth)
{
enter(depth);
// 进入某个房间
find() ? back() : search(depth + 1);
// 找着了吗
exit(depth)
// 退出某个房间
}
编译器识别尾递归,在开头添加标签并在最后更新参数后,添加的 goto 语句。
举个例子:
void print(int n)
{
if (n < 0)
return;
cout << n << endl;
print(n-1);
}
编译器优化代码,
void print(int n)
{
start:
if (n < 0)
return;
cout << n << endl;
n = n-1
goto start;
}
在编写递归代码时,为避免栈溢出可以采用静态计数控制。
void recursive(int data)
{
static int Depth = 0;
if(callDepth > MAX_DEPTH)
return;
callDepth++;
recursive(data);
Depth--;
}
depth 取决于函数堆栈帧大小和最大堆栈大小。