递归、堆和栈
标签(空格分隔): C 双学位高级语言程序设计 C函数
递归的基本思想
把规模较大的,较难解决的问题转化后才能规模较小的、易于解决的同类子问题。
规模较小的子问题又转化为规模更小的子问题,且小到一定程度可以直接得出它的解(递归的基本条件,又名终止条件、出口),从而得到原始问题的解。
数据结构中的“栈”
- 后进先出,先进后出
- 自顶向下移动指针
由于这个结构,函数调用时常用栈储存数据,叫做函数调用栈。
递归函数调用中,每一次都会更新函数调用栈中的数据。所以,每执行一次递归时,都会更新一次堆栈数据,运算会多计算一倍以上,所以递归时空效率偏低,易产生大量的重复计算。
尾递归
什么叫“尾递归”?当递归调用是整个函数体中最后执行的语句且它的返回值不属于任何表达式的一部分(即在回归阶段不需要任何计算)时,这种递归调用就是尾递归。
我们先来看两段求阶乘的代码
//代码1:普通递归
long Fact(int n)
{
if (n==0||n==1)
return 1;
else
return n*Fact(n-1);
}
//代码2:尾递归
long Fact(int n,int a)
{
if (n==0||n==1)
return a;
else
return Fact(n-1,n*a);
}
代码一为普通递归,计算复杂度更高。例如,像计算4!,它会先计算1!,再2!,3!,4!,因为返回值层层嵌套,每次乘以对应的n,对应的计算是4!=1*2*3*4;代码二为尾递归,每次都已经执行完了上一个函数任务。如上例,函数在每次递归时,并没有计算n乘以a,而是计算n乘以a。
从栈的分配上来说,代码一为普通递归,如果想要计算4!,就必须要计算3!,3!到2!,2!到1!,由于每次任务都未完成,所以必须要存储每次的栈针,而且退出失也要依次退出,这会堆叠很高的栈,容易数据溢出;而代码二为尾递归,每次任务都已经完成了计算,计算结果保存在a中,输出时一下子释放所有栈针,所以最后输出时不需要保存原栈针,节省了大量内存空间。