递归:
首先要明白栈的知识,“后进先出”
递归的话是程序运行到某个点的时候,调用自身,这个时候,之前没运行完的程序会暂时不运行,等到下一层调用完了之后再运行。这个正是符合栈的先进后出。这个时候就会有个进栈,等到下一层调用完运行了,之后就可以出栈继续运行。
其次要明白运用递归需要的两个条件:
一、要有边界,也就是出口
二、有递推关系
最后记住用到递归的要领:
利用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); //一步到位!变态
}