递归问题
定义:程序函数直接或间接调用自身
二、递归的注意事项
-----递归不是无限循环,递归的次数必须是有限的!-----
---如果没有终止条件,会发生栈溢出(StackOverflowError)---
-
每次执行一个方法会在JVM-栈中开辟一个内存空间,用于存放方法所需的数据,
-
比如方法的局部变量、对象引用
注意事项
-
(1)递归必须有出口(终止条件)
-
(2)每次递归,范围逐渐缩小
例子:计算累加和
-
public class RecursionDemo1 { /** * 计算累加和 * @param start,开始累加的值 * @param end,结束累加的值 * @return int 返回累加的结果 */ public static int addRecursion(int start,int end){ //递归出口 if (start == end){ return end; } //递归调用,每次递归,范围缩小 return start + addRecursion(start+1,end); } @Test public void testAddRecursion(){ int i = RecursionDemo1.addRecursion(1, 3); System.out.println(i); } }
-
-
方法执行完,出栈!
-
-
用递归还是循环
-
优势:简洁明了(用循环实现的用递归一定可以实现)
-
劣势:循环速度快(但有些场景循环实现不了,必须使用递归)
-
总结:优先使用循环,循环搞不定使用递归
-
三、递归的练习
1、实现斐波那契数列(求当前值和和)
(1)循环实现
//输出指定的值,以及所有值的总和 public static int feboUsewhile(int num) { //分析,当前的数的和,等于前2个数的和(NUM =1\2直接给定) int key = 0; int a = 1; int b = 1; int sum = 0; for (int i = 1; i <= num; i++) { if (i == 1 || i == 2) { key = 1; }else{ key = a + b; //计算当前的数的和 a = b; // 设置新的需要被加数 b = key; } sum += key; System.out.print(" " + key);//输入当前数的值 } return sum;//返回总和 // } }
(2)递归调用
//返回指定的值(递归) private int feboUseRecursion(int num){ //1、终止条件,num = 1或2时,返回值为1; if (num == 1){ return 1; }else if(num == 2){ return 1; } else{ //return value(num - 1) + value(num-2);前num-1个数的和+当前的值=前num-1个数的和 +(sum()) int i = feboUseRecursion(num -1) + feboUseRecursion(num -2); return i; } } //求和,循环 public int sum(int num){ int sum = 0; int value; for (int i = 1; i <= num; i++) { value = feboUseRecursion(i); sum += value; System.out.println(value); } return sum; } //另外附: //直接打印数量无返回值,num最小从1开始,one:0,two:1,index:1 private void feboUseRecursion2(int num,int one,int two,int index){ if (num == 1){ System.out.println(1); return; } //1、终止条件,num == index; if (index <= num){ int sum = one + two ; System.out.println(sum); feboUseRecursion2(num,two,sum,index+1); //return value(num - 1) + value(num-2);前num-1个数的和+当前的值=前num-1个数的和 +(sum()) } }
public void hannuotaRecursion(int num, char start, char mid, char end){ if (num == 1){ move(1,start,end); }else{ hannuotaRecursion(num-1,start,end,mid);//将N-1个盘子从开始移到中间 move(num,start,end); hannuotaRecursion(num-1,mid,start,end);//将N-1个盘子从中间移到最后 } }
四、总结
(1)递归可视为循环的简化升级版,注意
(1)辨别递归出口
(2)确定递归的内容,递归范围应该逐渐缩小,接近出口
(2)为了更好的理清思路,达到最终的实现。可以先分析循环的实现思路,并且在实现递归时,将功能拆分的尽可能细,循环能实现的,单独拎出来