您的位置:首页 > 其它

如何去理解递归,想到递归,运用递归

2013-08-24 22:44 260 查看

递归:

首先要明白栈的知识,“后进先出”

递归的话是程序运行到某个点的时候,调用自身,这个时候,之前没运行完的程序会暂时不运行,等到下一层调用完了之后再运行。这个正是符合栈的先进后出。这个时候就会有个进栈,等到下一层调用完运行了,之后就可以出栈继续运行。

其次要明白运用递归需要的两个条件:

一、要有边界,也就是出口

二、有递推关系

最后记住用到递归的要领:

利用A函数实现功能时, A函数体需要用到同样的功能,这时候就要用到递归

上边糊涂吧,利用下边的例子,保证你就能明白怎么用递归,什么时候用递归!Go!

举例子理解:

例1.strlen递归解法

先找递推关系:

如果想char *s="abcdefg",求strlen(s):

步骤1:求strlen("bcdefg"),也就是strlen(s+1)

步骤2:将步骤1的结果加1返回即可

------------这就是递推关系,求s长度,需要求s+1长度 !!!

那边界条件呢:

当char *s=NULL或者char *s=‘\0’时,直接得到结果,而不用继续递归下去,这就是边界条件

分析好了,代码自然就出来了

int strlen(const char * s)
{
//处理边界条件
if(NULL==s)
return -1;
else if('\0'==*s)
return 0;
//递推关系
else
return (strlen(s+1)+1);
}


例2:递归实现字符串逆序

比如:s=“abcdef”,逆序后变为“fedcba”

先找递推关系:

如果想逆序abcdef:

步骤1:将a和f进行交换

步骤2:对bcde进行逆序-------(需要同样的功能,所以必须用递归)

------------这就是递推关系,逆序“abcdef”,需要逆序“bcde”!!! 进一步可知,此递归函数得输入参数上标和下标

那边界条件呢?

当s里边无字符,也即为NULL情况下,不用排序

当s里边只有一个元素时,也不用排序,这就是边界条件

分析好了,代码自然就出来了:

char * Reverse_Str(char *s, int first ,int last)
{
//边界条件:利用上标下标判定
if(last<=first)
return s;
else
{
//交换首尾字符
char temp=s[first];
s[first]=s[last];
s[last]=temp;
//逆序中间部分
Reverse_Str(s,first+1,last+1);  //注意这里千万,不要first++,和last--,为什么,自己琢磨吧!

}
}


例3. 汉诺塔问题

 

先找递推关系:

               如果想n个盘子,先放在A,利用B,原样的放在C:

                                        步骤1:将A的最上方n-1个盘子,通过C,放在B中

                                        步骤2:将A的最上方那个盘子,通过B,放在C中

                                        步骤3:将B中那n-1个盘子,通过A,放在C中

------------这就是递推关系,函数的功能是,将A中的盘子,通过B,放在C中,而我们移动的每一个步骤,都是需要同样的功能。进一步得知,函数的参数,应该有A、B、C

    

那边界条件呢:

边界条件就是,只有一个盘子时,移动一次就够了。代码里表示,也就是输出,从哪移动到哪

分析好了,代码自然就出来了:

void hanoi(int n,char A,char B, char C)
{
//边界条件
if(1==n)
{
printf("%c-------%c\n",A,C);
}
else
{
hanoi(n-1,A,C,B);//步骤1
hanoi(1,A,B,C); //步骤2
hanoi(n-1,B,A,C);//步骤3
}
}


例4:通用树遍历显示

先了解一下: 

文本的方式显示一颗树

A

---------B

----------------------E

----------------------F

---------C

---------D

----------------------H

----------------------I

----------------------J

A有子树节点B C D

B有子树节点E F

D有子树节点H I J

整个输出打印过程:

打印A结点

|

打印A结点的---孩子结点(具体:打印第一个孩子结点B,再打印B--的--孩子结点,接着同样的工作-打印孩子结点 C D.......)

单独拿出来,打印B孩子的结点分为:

打印B结点

|

打印B结点的孩子结点

对A,对B,打印执行的功能,是一样的

这就是一个递归!


先找递推关系:

                            

                                        步骤1:找到根节点,输出

                                        步骤2:找到孩子结点,输出

                                        

------------这就是递推关系,函数的功能是,先打印父结点,再打印孩子结点。而我们打印孩子结点时,需要同样的功能。

                     

static int recurse_display(GTreeNode * node,int format)
{
int i;
if(NULL!=node)
{
//1.打印根结点

//1.1先打印占格符
for(i=0;i<format;i++)
{
printf("%c",'-');

}
//1.2.打印父节点
printf("%c",(int)(node->data));

//1.3.换行
printf("\n");

//2.打印子节点

for(i=0;i<LinkList_Length(node->child);i++)
{
//2.1获取子结点
TLNode* trnode=(TLNode*)LinkList_Get(node->child,i+1);
//2.2打印子节点的孩子结点
recurse_display(trnode->node,format+4);

}

}
}


例5. 求1+2+…+n

要求不能使用乘除法、for、while、if、else、switch、case等关键字以及条件判断语句(A?B:C)。
这道题递归一般的求解是:
先找递推关系:
如果想求1+2+.....+n:
步骤1:1+2+..+n-1
步骤2:n+步骤1的值
------------这就是递推关系,求值1+2++......+n,需要逆求1+2+.....+n-1 !!!

那边界条件呢?
当n小于等于0时,和为0
这就是边界条件

long getSumofN (int n)
{
if(n<=0)
return 0;
else
return ( n+getSumofN(n-1) );
}


这里出现if else,不满足题意的,因此需要解决两个问题,如何判断边界条件,以及如何存储每次计算所得值

解决方法:执行---逻辑判断--语句

int getSumofN(int n, int *result)
{
n&&getSumofN(n-1,result);    //前者用来判断边界,后者存储,执行步骤1
return (*resulet +=n);   //执行步骤2
}


进一步给力:

int getSumofN(int n, int *result)
{

return n&&getSumofN(n-1,result)&&(*resulet +=n);   //一步到位!变态
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  递归