最近看了一下关于递归的,还是很有意思。
那么什么是递归呢?
程序调用自身的编程技巧称为递归( recursion)。
一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
(1) 递归就是在过程或函数里调用自身;
(2) 在使用递归策略时,必须有一个明确的递归结束条件,
称为递归出口
还是先从求n!开始吧!
- #include "stdafx.h"
- #include <iostream.h>
- int fun(int n)
- {
- if (n==1)//递归有个规则,就是需要一个递归出口,要不就会永远的递归下去了
- {
- return 1;
- }
- return fun(n-1)*n;
- }
- int main(int argc, char* argv[])
- {
- int n=5;
- cout<<fun(5)<<endl;
- return 0;
- }
结果是120.·这个程序还是比较好理解,fun函数里面的n不断减1,并进行fun(n-1)*n的运算,直到n==1,我们可以把它的进度记录下来:
n=5时,return fun(4)*5
n=4时,return fun(3)*4 //fun(3)*4和fun(4)是等价的
n=3时,return fun(2)*3 //同上
n=2
时,return fun(1)*2 //同上
n=1时,return 1 //即fun(1)=1 此时已经没有继续递归下去了,该函数结束。
所以返回fun(n-1)*n的值的时候等于1*2*3*4*5的值。
接下来我们看一下斐波那契数列。(代码借用)
- #include "stdafx.h"
- #include <iostream.h>
- int fun(int n)
- {
- if (n==1||n==2)
- {
- return 1;
- }
- return fun(n-1)+fun(n-2);
- }
- int main(int argc, char* argv[])
- {
- int n=10;//求的是第10个数是几?
- cout<<fun(n)<<endl;
- return 0;
- }
原理跟上面一样,不过是前一个加上后一个。不过这个过程看起来,就像一个树根一样,不断地向下延伸,读者可以想象一下,一个(函数)变两个,两个变四个。
最后当n==1或者n==2时结束,因为
fun(n-1)和fun(n-2)是同时进行的,故需要两个判断条件。多个fun函数才能结束。
不知道大家发现没有。。。。递归实现了某种类型的螺旋状while循环。while循环在循环体每次执行时必须取得某种进展,逐步迫近循环终止条件。递归函数也是如此,它在每次递归调用后必须越来越接近某种限制条件。当然我们在写递归程序的时候也要注意考虑,这个问题能不能用递归,这个问题的限制条件是什么。
最近做了一个题:
题目内容:
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?M, N为自然数。说明:如有7个苹果,2个盘子,则(5, 1, 1)和(1, 5, 1)和(1, 1, 5)都是同一种分法。
解题分析: 9 设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论, 10 当n>m:必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响。即if(n>m) f(m,n) = f(m,m) 11 当n<=m:不同的放法可以分成两类: 12 1、有至少一个盘子空着,即相当于f(m,n) = f(m,n-1); 13 2、所有盘子都有苹果,相当于可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n). 14 而总的放苹果的放法数目等于两者的和,即 f(m,n) =f(m,n-1)+f(m-n,n) 15 递归出口条件说明: 16 当n=1时,所有苹果都必须放在一个盘子里,所以返回1; 17 当没有苹果可放时,定义为1种放法; 18 递归的两条路,第一条n会逐渐减少,终会到达出口n==1; 19 第二条m会逐渐减少,因为n>m时,我们会return f(m,m) 所以终会到达出口m==0.
程序代码
#include<stdio.h>
int fun(int m,int n)
{
if(m==0||n==1)
return 1;
if(n>m)
return fun(m,m);
else
return fun(m,n-1)+fun(m-n,n);
}
int main()
{
intt,m,n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&m);
scanf("%d",&n);
printf("%d\n",fun(m,n));
}
}
仔细把这个题研究一下,应该就知道什么时候用递归了。
递归和迭代
举个例子:
斐波那契数列:1,1,2,3,5,8,13,21,34......
迭代:int Fib[N];
Fib[0]=1;Fib[1]=1;
for(i=2;i<N;i++)
Fib[i]=Fib[i-1]+Fib[i-2];
}
递归:int Fib(int n)
{ if(n==0||n==1)return 1;
else return (Fib(n-1)+Fib(n-2));
}
递归坏处:由于递归需要系统堆栈,所以空间消耗要比非递归代码要大很多。而且,如果递归深度太大,可能系统撑不住。
小的简单的用循环。
太复杂了就递归吧,,免得循环看不懂。
太复杂了就递归吧,,免得循环看不懂。