从零开始的递归入门教学

递归的定义与实现原理

定义:函数调用自身的编程技巧称为递归
在这里插入图片描述


为什么函数要调用自己呢?

答:因为递归的下一层状态是由上一层推来的,函数一次次调用自己、一层层推下去一直到末尾就能得到答案


那既然是从一个起始状态一直推到末尾状态,那为啥不用循环而要用递归呢?

答:因为递归的灵魂在于 “归” 字,每次走到末状态后回到上一层,这个过程就叫 回溯 ,每次走到结尾更新答案后,回溯到上一层,上一层可以 继续递归调用到另外一种状态 ,这种返回上一层再推到下一层的操作是循环无法完成的

可能已经看晕了,那我们看图理解

例①:求ABC三个字母的所有排列
那么它的求解过程应该是
在这里插入图片描述
从起点开始,一路走A —> AB —> ABC,在走到第一个末尾ABC的时候,又要重新回到上一层AB,发现这层到不了其他状态了,于是再回到上一层A,发现A还可以到AC,于是又递归到下一层状态AC去,以此类推。

思考一下如果用for循环写,就需要3层for循环,那假如有26个字母,那就是26层for循环。。。

但如果是递归,当回溯到 A 后,又可以直接递归到AC


那这个递归这么神奇,它是怎么实现的呢?

答:我们在开头说了,递归是函数自己调用自己,那不就死循环了吗?说得对!所以为了防止死循环,在函数每次调用自己的时候都要告诉下一层它目前的状态,然后判断这个状态是不是结尾,如果是的话就结束递归

还是拿上一个例子说明,ABC三个字母排列,状态肯定是长度,初始长度是0(因为啥字母都没加呢),加了A之后,调用下一层,并对下一层说 :现在的长度是1,我们只要3就够了,千万别加多了,然后第二层加了个 B 变成了 AB,第二层也照葫芦画瓢的对下一层说 现在长度是2,一直到最后变成ABC之后,下一层听到上一层说长度已经到 3 了,它就不继续加了,而是直接 r e t u r n return return 回去

代码如下

char s[5]; 
int vis[5];
void solve(int len=0){
	if(len==3){//判断是不是结尾
		printf("%s\n",s);
		return ;//是就不用加了,直接输出答案并返回上一层
	}
	for(int i=0;i<3;i++){
		if(vis[i])continue;
		s[len]='a'+i;
		vis[i]=1;//标记这个字母已经用过了
		solve(len+1);//加了字母之后调用下一层并告诉它现在的长度
		vis[i]=0;//这个字母找完了要把标记还原
	}
}

根据上面的例子,我们了解了,递归有如下几个步骤

  1. 找到初状态,末状态,状态的变化
  2. 判断是否到达末状态,若是结束当前状态,不再继续到下一层,若不是则执行步骤3
  3. 选取下一个合法的状态继续向下一层递归执行步骤 2 2 2,并告诉下一层目前的状态,若找不到合法状态,就返回上一层执行步骤 3 3 3

就是这么一个套娃的过程,直到所有状态都找完程序就结束了


什么时候需要用递归呢?

答:你可以根据题目要求画个图,只要找答案的过程是一个 树形 的结构就需要用到递归


例题

bzoj 1621 分岔路口
题意:
初始一共有 N N N 头奶牛,每到一个分岔路口,如果这群奶牛的数量可以分成 X X X X + K X+K X+K的话,就分裂并继续往下走,否则就留在原地,问最后这群奶牛最后分成了多少群

Example

input 
6 2
output
3

样例是这样的
在这里插入图片描述

看到这个树形的结构,很明显是个递归问题,我们按照前面说的步骤来:

初状态就是奶牛未分裂的数量 N N N
末状态是 A A A 不能分成 B B B B + K ( 1 ≤ B ) B+K(1 \le B) B+K(1B) ,化简一下就是 A ≤ K + 2 A\le K+2 AK+2 ( A − K ) % 2 = 0 (A-K)\%2=0 (AK)%2=0
状态变化是当前奶牛数量 A A A 分为两个相差 K K K 的数,即 ( A − K ) / 2 (A-K)/2 (AK)/2 ( A + K ) / 2 (A+K)/2 (A+K)/2

int solve(int A){
	if(A-K<2||(A-K)%2==1){
		//到了末状态,返回这一状态的答案,不继续往下递归
		return 1;
	}
	//否则就继续到下一层状态去
	int ans=solve((A-K)/2)+solve((A+K)/2);
	//这一层的答案就是它分裂的两堆的答案的和
	return ans; 
}
int main(){
	int ans=solve(N);
}

对应到样例那张图就是
在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值