相信看到这篇文章的大家已经把我上一篇文章给看了(如果没看请先看上一篇以便于理解),接下来我们接着上一篇文章进行讲解。首先,我上一篇文章的递归函数测试代码输入20时程序也会报错(见图一),这更加验证了每调用一个函数都会分配"栈帧"。那么看到这里肯定有人会问了,“你这个栈空间和我递归函数有什么关系?”,别急,关系很大。理解栈空间以后我们就能深刻理解函数的调用以及我们递归函数的优缺点。接下来就是我们的重头戏“递归函数”,在讲解递归函数的时候我也会把函数调用关系也更加深入的说明一下。首先废话不多说,我们直接上代码。这是一个递归函数求斐波拉契数列的第n项的代码(因为也比较简单而且也很契合我们的文章)。
#include<iostream>
using namespace std;
long long fab(int n)
{
long long sum;
if (n == 1 || n == 2)
{
return 1;
}
sum = fab(n - 1) + fab(n - 2); //斐波拉契数列规律(第n项等于第n-1项加第n-2项)
return sum;
}
int main()
{
int n;
cout << "请输入你要求的项为:" << endl;
cin >> n;
cout << "第" << n << "项的值为" << fab(n) << endl;
return 0;
}
接下来我们输入我们想知道的项(为了方便计算输入了6)。结果也是和我们预想的一样。但是我们输入50的时候,他就一直卡在那里光标不停闪烁(图二)。这时候就有人问了。唉?那这是为什么呢?是不是你的程序出现bug了?然而答案并不是这样的,其实这里计算机还是在奋力的计算,但是为什么卡住呢?这个就是我们的递归函数的缺点了,接下来回到我们栈空间的话题,我们知道每调用一个函数都会分配一个栈帧,但是每次分配“栈”计算机底层都会做大量工作(保护原来的执行环境,切换到新的执行环境),会有一定的时间开销。因此我们的递归函数被连续调用五十次的时候我们的终端就会像卡顿了一样停在那里不出结果(执行效率很低)。虽然我说了那么多,但没有一句是废话。这样我们就能理解递归函数的缺点了。唉?说了缺点那么优点呢?别急,等我们先理解递归函数这么运行的(也就是函数调用的深刻理解)。我相信我上面的代码肯定有新手有疑惑(甚至一些有一定开发经验的人),我们直接上代码来理解他。
#include<iostream>
using namespace std;
void tunNnel(int n)
{
cout << "进入第" << n << "条通道" << endl;
if (n > 5)
{
return;
}
tunNnel(n + 1);
cout << "退出第" << n << "条通道" << endl;
}
int main()
{
tunNnel(1);
return 0;
}
运行结果如图三所示。那么看到这里的人有没有猜到呢?我们可以看到第一次我们传参数1进入函数然后打印第一句话,然后判断结束条件。在进入下一个函数,直到进入最后一个函数(第n个个函数),然后开始返回上一个函数调用位置(n-1)。以此类推我们可以深刻的理解函数调用的关系,以及递归函数的用发,也能解释的通上一个代码的结果。
接下来我将讲解递归函数的用法,递归函数什么时候用呢?一般的开发中我们能不用就不用,没错,你没看错。理由就是运行效率实在是太低了。那么该什么时候用呢?重点:当我们无法直接解决问题的时候,我们可以将问题拆解成问题本身,拆解后的难度更小或者规模更低的时候可以用。那么我们该怎么设计递归函数呢?首先我们要确定结束条件,其次就是我们上面的重点,依照我上面的话设计内容即可。
好了,不积跬步无以至千里,希望大家看完这篇文章以后有所收获。如果可以不妨点亮大拇指,让好文章更多的传递出去。凌晨写文章脑子难免有点昏昏沉沉,有些地方可能有一点小遗漏。以后想到什么会和大家补充(前提是这篇效果不错)。
至此递归函数以及栈空间的坑已经填完,如果全部看懂了那么你又离高级程序员近了一步。谢谢大家观看-----------------------------------------------------玉无涯