蓝桥杯学习——递归与循环
递归与循环的区别
递归,说白了就是自己调用自己,利用的是栈后进先出的原理。理论上,任何的循环都可以重写为递归形式,所有的递归也可以被表述成循环的形式
循环改递归的两大要点
- 发现逻辑循环中的“相似性”
- 找到递归的**“出口”**
- 确定递归的参数
踢皮球思想理解递归
“踢皮球”-常用来形容政府职能部门职责不清,相互推诿,比如你要到部门A盖个章,A叫你先去部门B盖章,B叫你去部门C盖章…这种将工作以踢皮球的方式甩给其他人做的方式其实很适用于递归。但在递归中,如果你甩给其他人的工作和分配给你的工作完全一样,则程序陷入死循环,一直没有人进行工作,一直在甩锅,因此,递归中为:**自己做一小部分事情,剩下的事情再甩给其他人去做。**即上级做玩自己的工作然后交给下级去做。
递归调用
- 递归调用仅仅是被调用函数恰为主调函数(自己调用自己)
- 注意每次调用的层次不同
- 注意每次分配形参并非同一个变量
- 注意返回的次序
练习
试题 基础练习 01字串
问题描述
对于长度为5位的一个01串,每一位都可能是0或1,一共有32种可能。它们的前几个是:
00000
00001
00010
00011
00100
请按从小到大的顺序输出这32种01串。
输入格式
本试题没有输入。
输出格式
输出32行,按从小到大的顺序每行一个长度为5的01串。
样例输出
00000
00001
00010
00011
<以下部分省略>
循环解决
思路:暴力枚举
public class Main {
public static void main(String[] args) {
for(int i=0;i<=1;i++){
for(int j=0;j<=1;j++){
for(int k=0;k<=1;k++){
for(int l=0;l<=1;l++){
for(int m=0;m<=1;m++){
System.out.print(i);
System.out.print(j);
System.out.print(k);
System.out.print(l);
System.out.println(m);//注意前面的都是print,最后一个用println
}
}
}
}
}
}
}
递归解决
思路:初始化目标字符串为空,长度为0,每次递归执行增加一个字符0和1两种情况,当字符长度达到5时结束递归
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
static ArrayList<String>conArrayList=new ArrayList<>();
public static void f(String s,int k) {
if(k==5) {
conArrayList.add(s);
}
else {
f(s+'0', k+1);
f(s+'1', k+1);
}
}
public static void main(String[] args) {
f("",0);
for(String s:conArrayList) {
System.out.println(s);
}
}
}
学习参考
https://www.bilibili.com/video/BV1ty4y117go?t=885&p=2
https://blog.csdn.net/Upwardflow/article/details/81561124
https://www.jianshu.com/p/ebd9e232f044
学习心得
时间和空间消耗
递归由于是函数调用自身,而函数调用时是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址及临时变量,而且往栈里压入数据和弹出数据都需要时间。这就不难理解有些时候递归实现的效率不如循环。所以相对于循环,递归空间和时间一般占用量比较大。但是相对于循环,递归更加简洁明了。
重复的计算
递归有可能很多计算都是重复的,从而对性能带来很大的负面影响。递归实际上是把一个大问题拆成很多小问题,如果那些小问题存在相互重叠的部分,就存在重复的计算。可以采用保存吗,每次递归计算结果的方式进行优化。
调用栈溢出
除效率之外,递归还有可能引起调用栈溢出。前面提到了需要为每一次函数调用在内存栈中分配空间,而每个进程的栈的容量是有限的。当递归调用的层级太多时,就会超出栈的容量,从而导致调用栈溢出。