目录
一、递归的基本概念
对于有接触过c语言的,对递归应该是具有一定的了解,在Java中的递归跟c语言中没什么很大的区别。
递归这种思想,大多可以用在一些编程里面,帮助我们更灵活的实现功能,但有时反而会使代码运行效率降低,在什么场合中使用,需要我们通过不断刷题和学习,培养我们的思维。对我们以后编程能力的提升有很大的帮助!
递归:
一个方法,在其本身内执行时又调用了自身的方法,就被称为”递归“。
【注意:”递“和”归“是两个动作】
进行递归时,必须要有一个可以结束的条件和调用自己本身,否则将不断调用本身,直到栈溢出!
简单递归代码示例:
1+2+3+...+10
可能当当代码有点难理解期间”递“和”归“的过程,接下来我将把递归的过程拆开分析。
二、递归执行的过程分析
我们在分析递归的过程时,以平时的纵向思考可能难以理解,反之以一种横向思考,反而相得益彰。
需要从两个方向点着手:(1)递归的起始条件或终止条件;
(2)方法执行结束再调用本身方法流程(推导递推公式)
以上面的代码进行过程分析:
(1)在方法第一次调用时,我们传过去的参数为10,进行if判断后(不满足等于1),执行下面的return语句,而return语句又包含了本身方法 return 10+backOf(9);之后就会再进行第二次调用,但这次传过去的参数变成了9,以此不断往后反复调用.
(2)在到2+backOf(1)时,backOf(1)的方法调用,其方法内部满足(n==1),返回一个1给该方法backOf。
(3)之后会不断进行”归“的过程,直到方法结束.
期间过程:
1. 10+backOf(9)
2. 10+9+backOf(8)
3. 10+9+8+backOf(7)
4. 10+9+8+7+backOf(6)
5. 10+9+8+7+6+backOf(5)
6. 10+9+8+7+6+5+backOf(4)
7. 10+9+8+7+6+5+4+backOf(3)
8. 10+9+8+7+6+5+4+3+backOf(2)
9. 10+9+8+7+6+5+4+3+2+backOf(1)
10. 10+9+8+7+6+5+4+3+2+1
程序依此进行后,最终结果即为1+2+3+...+10=55
【总结】:
在上面这个代码递归的过程中:
- 我们要先找出递归过程中的起始位置,以上面代码为例,n==1(是判断返回的终止条件,从某种意义上来看也算起始条件),如果连该条件都找不到,就无法实现递归,不能无限制地调用本身,必须有个出口
- 找到之后,还得根据一定的自身调用(此代码中,不满足等于1,继续进行调用)进行依次传递(递归前进段)。
- 最关键的还是,要理解清楚递归, 必须先理解清楚 "方法的执行过程", 尤其是 "方法执行结束 之后, 回到调用位置继续往下执行".依次进行”归“后,将各个方法的返回值进行相加,最后返回到主方法内。
三、递归练习
1、递归依次按顺序打印1234
要想依次打印出每一位数,首先可以联想到取余的方法,然后利用除法将位数减少,再不断提取每一位。
可如果是要求递归的话,必须要有一个终止条件,在调用本身的某一处停止后返回。
代码示例:
public class Review {
public static void Print(int n) {
if(n<10) {
System.out.print(n+" "); //当调用该值小于10,说明该值已经除完
return;
}
Print(n/10); //调用到最后从最高位开始依次打印
System.out.print(n%10+" "); //执行打印到最低为
}
public static void main(String[] args) {
Print(1234);
}
}
该处递归过程很好理解,无非就是先找到终止条件(最高位1),然后以它判断后再调用本身,每一次调用就除10,减少一位后接打印其取余。
(1) n=1234 ----> Print(1234/10) [123]
System.out.print(1234%10+" "); [4]
(2)n=123 ----> Print(123/10) [12]
System.out.print(123%10+" "); [3]
(3)n=12 ----> Print(12/10) [1]
System.out.print(12%10+" "); [2]
(4)n=1 ----> n<10 System.out.print(1+" "); [1]
return ;
条件里的return必不可少,否则递归停不下来。
又因为调用方法在每次打印取余前面,最后是从1开始打印,依次最后到打印4。
2、递归求 N 的阶乘
代码示例:
import java.util.Scanner;
public class TestDemo6 {public static int addsum(int n) {
if(n==1) {
return 1;
}
return addsum(n-1)*n;
}
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入N:");
int n=scanner.nextInt();
System.out.println(n+"的阶乘为:"+addsum(n));
}
}
只要知道着手方向,再根据终止条件和循环调用的边界,像这类的简单递归都是很好理解的。
像此处的阶乘递归,也就是不断调用自身的下一个数,到最后为1的时候返回1,其结果即为10*9*8*7*6*5*4*3*2*1=120
总结:
递归虽然很方便的让我们能更灵活、更容易的实现所想要的功能运算,但每一次递归都会开辟一个空间,随着递归不断进行,递归会开始力不从心,在某一时刻造成内存负担,使我们运行的效率降低。不是所有的运算程序都适用递归,比如斐波那契数列为例,如果采用递归的话,会随着所找的第N项,不断进行大量重复的递归,加重内存负担,大大降低了程序的执行速度。
递归有递归的优点(简洁),也存在缺点(代码难以理解,有时随着代码的复杂程度会更难理解),因此我们在平时要善用递归,不能说想用就用。
但也不乏一些特殊情况,必须得使用递归才可以的程序(比如二叉树)
而对于递归的题型,不是就上面如此简单,以后我们还会碰到更加难以理解的递归程序,因此需要我们不断刷题总结,提升,培养自己的思维能力,才能事半功倍。