递归

按我理解,递归就是不停地调用自身,到一定的临界条件后得出结果。


按照百度的说法,

递归一般用于解决三类问题:

  (1)数据的定义是按递归定义的。(Fibonacci函数)
  (2)问题解法按递归算法实现。(回溯)
  (3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)


递归的缺点:

  递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。


递归的四条基本法则:

1.基准情形。必须总有某些基准情形,它无须递归就能解出。
2.不断推进。对于那些需要递归求解的情形,每一次递归调用都必须要使求解状况朝接近基准情形的方向推进。
3.设计法则。假设所有的递归调用都能运行。
4.合成效益法则,在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。
一定程度上,递归能转化为迭代,但更多的是引入stack这个结构。


如何设计递归算法

  1.确定递归公式
  2.确定边界(终了)条件


计算n!

#include 
long factorial(long n)
{
 if (n == 0 || n == 1)
  return 1;
 else
  return n * factorial(n-1);
}
int main(void)
{
 int n = 0, i = 0;
 long sum = 0;
 printf("Please input a number: \n");
 scanf("%d", &n);
 printf("%d! = %lu\n", n, factorial(n));
 return 0;
}

我看到过的其中的一条面试题:

楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法.

如果只有1个台阶,只有1一种走法。

如果有2个台阶,就有11和2两种走法。

如果有3个台阶,有111,12和21三种走法。

如果有4个台阶,有1111,112,121,211,22五种走法。

如果有5个台阶,有11111,1112,1121,1211,2111,122,212,221八种走法。

这个与斐波那契(Fibonacci)数列有关,斐波那契—卢卡斯递推:f(1)=1,f(2)=2,从第三项开始,每一项都等于前两项之和f(n) = f(n-1)+ f(n-2)(当n>2)。

所以

          1         n=1 
f(n)={ 2       n=2
     f(n-1)+f(n-2) n>2

int f(int n)
{
    if (n == 1 || n == 2)
        return n;
    return f(n-1) + f(n-2);    
}

关于递归消耗的内存

递归更消耗内存,递归无非是程序调用自身,程序运行时代码和数据存到内存中,即要分配内存空间,递归程序每调用一次自身,操作系统都要为其分配相应的内存资源,这一过程是通过“栈”这种数据结构实现的,栈就像一个水桶,只有上面有口,进栈和出栈都只能通过上面的口,从而有先进后出的数据结构特性,好啦,现在再回到递归那里,递归程序的一次调用好比向桶里放一块砖,依次向上堆,调用不结束就会一直向里放“砖”,如果调用次数稍多或内存较小的话很容易溢出,你会发现每次调用有很多东西是重复的,而循环则不同,循环得每次执行是对已有变量的更新,一般不会产生大量重复的东西,从而节省内存,但你会问我,为什么还要用递归呢,这是因为一些算法用递归写起来比较方便,当然不少递归算法都有他的循环版本,在嵌入式开发中,禁止写递归。


你可能感兴趣的:(【算法】)