我前面有很多文章一直说,递归和循环是一样的,可以相互转换。
循环是一直重复某个语句,递归是一直调用自己。要想转化,递归就得好好想想自己的内部代码和循环执行的那句代码的关系(应当一样)。
1.打印单链表
非递归
void printLnode (l* p){
while(p){
print(p);
p = p->next;
}
}
递归
void printLnode (l* p){
print(p);
printLnode (p->next);
}
内容上,打印的代码不变,循环移动的代码,变成了递归。
这里打印语句一定要理解为函数的功能,这样写复杂的递归时才能又快有准。
打印当前;
打印下一个;
我们发现一个问题,这个函数一直调用自己,没有退出的情况,不符合实际呀,所以,递归之后,加上一个出口就好了,递归的套路就是这样了。
void printLnode (l* p){
if(p){
print(p);
printLnode (p->next);
}
else return;
}
//或者
void printLnode (l* p){
if(!p){
return;
}
else {
print(p);
printLnode (p->next);
}
}
我们这就完成了访问。
上面有个模糊的点,因为函数的返回值是void,所以,我们就没提到返回值这个玩意。
在while循环中,我们很清楚的知道,当p为空时,函数会执行到底,函数结束,就是所有的东西结束了,在递归当中呢?
如果把递归理解为功能,那么,要是p为空就不执行该功能,不为空则打印,并,准备下一次打印。然后再从尾到头一个个结束所有函数,直到回到第一个函数(即使所有函数执行完就是结束)。
那我们要计算结点个数呢?
2.计算结点个数
非递归
int count = 0
int printLnode (l* p){
while(p){
count = count + 1; //print(p);
p = p->next;
}
return count;
}
递归
int count = 0
int countLnode (l* p){
if(!p) {
return 0;
}else{
//count = count + 1; //print(p);
return countLnode (p->next) + 1;
}
}
这里有一个非常大的不同点,就是返回值。
循环和递归的主要区别,循环是从上到下执行,执行结束一个返回,而递归?从上到下执行,执行完之后,从下到上依次返回。也就是,最后返回值一定的第一次进入的函数,那么这时候,想把最后的(最里面的函数)返回值带出来,怎么办?
就得在return语句上下功夫了。已知:return是一个函数最后的返回值,在递归当中,一定的最里面的函数先return,最后才是第一个函数return,统计节点的时候,相当于从最后一个开始回归,0,1,2,3…等回到第一个的时候就是个数了。
所以countLnode (p->next) + 1是写在return语句中的
这种写法是根据递归的运行过程来的,没有用到count计数器,如果从代码角度来看,用count计数器该如何实现呢?
递归
int count = 0
int countLnode (l* p){
if(!p) {
return 0;
}else{
count = countLnode (p->next) + 1;
return count;
}
}
这里有没有发现盲点,计数器的作用不是原来的count+1了,如果写成原来的,那么他的值是不会变的,只有和上一次的变化有关,才能统计出个数,递归的统计就是从后向前的统计,count = countLnode (p->next) + 1代码依旧是从后向前统计的。
本文重点分析了:
- 循环和递归的转换
- 无返回值的递归和有返回值的递归
- 有返回值的递归直接返回和有返回值的递归间接返回
下一篇分析二叉树的思考。