如何去理解递归,想到递归,运用递归
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); //一步到位!变态 }
相关文章推荐
- 如何理解和熟练运用js中的call及apply?
- 递归算法--如何一步一步理解递归(1)
- 如何理解和熟练运用js中的call及apply
- 如何通俗理解递归
- 如何理解和熟练运用js中的call及apply?
- 如何理解和熟练运用js中的call及apply?
- 递归算法--如何一步一步理解递归(2)--全排列
- 如何通俗的理解递归
- 由合并排序算法谈如何理解递归
- 如何理解和熟练运用js中的call及apply?
- 如何理解和熟练运用js中的call及apply?
- 如何理解递归?
- 如何理解递归
- 如何理解和熟练运用js中的call及apply?
- 到底如何理解递归?
- 如何理解和熟练运用js中的call及apply?
- 摘要:我们经常会用到递归函数,但是如果递归深度太大时,往往导致栈溢出。而递归深度往往不太容易把握,所以比较安全一点的做法就是:用循环代替递归。文章最后的原文里面讲了如何用10步实现这个过程,相当精彩。本文翻译了这篇文章,并加了自己的一点注释和理解。
- 由递归所想到的:如何将字符串或者数字转换成大写货币的问题
- 如何理解递归
- 如何理解汉诺塔的递归?