学习递归思想以及一些相关题。
题:阶乘 汉诺塔 青蛙跳台 斐波那契数列 输出数字 递归求 1 + 2 + 3 + ... + 10 返回组成它的数字之和(1234-->1+2+3+4=10) 递归打印1到9的数字
目录
按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
嵌套:方法里面调用方法;a方法里面调用了b方法或者c方法或者其他方法。
递归:特殊的嵌套,方法里面调用了自己(方法);a方法里面调用了a方法本身;
理解:有点像俄罗斯套娃一样,大的里面不断套小的,但是一整套套娃的类型都是一样的(都是一个模样),递归也是如此。当遇到一个大问题解决不了,可以将它拆分成小问题,而小问题与大问题有相同的解决方案,那么小问题解决后,大问题不就解决了。
必要条件:
1.已知最小子问题的答案(初始条件),得让递推公式停下来的条件。
2.能够不断将大问题划分成更小的子问题(有递推公式)
初阶:刚开始做的时候没有太多的思路,先写出他的初始条件,在调用它的方法,然后画图去理解,看哪里不对就去修改,最终改为正确答案。
进阶:已经差不多了,就可以去思考,首先是个大问题,我不断拆成小问题(比如递归求1+...+9的和,那我可以想去思考1+...+8的和,接着可以思考1的和),我还能想到最小子问题的答案(此时初始条件就知道了)。(方法就是最后求解的答案,也就是该问题的答案)
N的阶乘
//n的阶乘 public static int fac(int n){ if(n == 1){ return 1; } int result = n*fac(n-1); return result; } public static void main(String[] args){ int ret = fac(3); System.out.println(ret); }
按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
//按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4) public static void numPrint(int n){ if(n / 10 == 0){ System.out.print(n+ " ");//输出最高位 return ; } numPrint(n / 10); System.out.print(n % 10+" "); return ; } public static void main(String[] args){ numPrint(1234); }
递归求 1 + 2 + 3 + ... + 10
//递归求 1 + 2 + 3 + ... + 10 public static int sumFac(int n){ //问题可以拆成 1+..+9 最小就是当遇到1 if(n == 1) return n; int ret = n + sumFac(n - 1); return ret; } public static void main(String[] args) { int ret = sumFac(10); System.out.println(ret); }
有种感觉:本身要求1+...+10,那我可以先写成10+ 剩余的(1+...+9),是不是就拆成小问题了。
递归打印1到9的数字
//递归打印1到9的数字 public static void printNum(int n){ //要打印1-9,如何打印1-8...那就得考虑如何打印1 if(n == 1){ System.out.print(n+" "); return ; } //从1-9一直到先打印1 printNum(n - 1); System.out.print(n+" "); return; } public static void main(String[] args){ printNum(9); }
返回组成它的数字之和(1234-->1+2+3+4=10)
思路:
1.找初始条件:让我求1234组成的数字之和,我得先求123组成的之和,我得求12,得求1,因此我得先找到最高位 当n/10==0 时就是最高位了,我就返回它。
2.找递推公式:既然是递推,那肯定要用到这个方法,我又知道要让数字1234,变为123,变为12...那我就得让该方法里的数字num/10才能变小。而该方法是返回单个位的值的,拿我需要和上一位相加num % 12,然后就是返回。大概可以这样理解//返回组成它的数字之和(1234-->1+2+3+4=10) public static int numSum(int num){ //求1234的和 我得先求123 子问题也就是找一位 if(num / 10 == 0){ //或者if(num < 10) return num;//把最高位返回去 } int sum = num % 10 + numSum(num / 10);//从1234 不断缩小123 12 1 并且和前一位相加 return sum; } public static void main(String[] args){ int sum = numSum(1234); System.out.println(sum); }
斐波那契数列
1 1 2 3 5...每一项等于前两项之和(除了第1和第2项)
//递归求斐波那契数列第n项 public static int fib(int n) { //第n项不会求,我会求第一第二项 if(n == 1 || n == 2){ return 1; } //第n项不就等于第n-1项加上n-2项么 int num = fib(n - 1) + fib(n - 2); return num; } public static void main(String[] args) { int num = fib(6); System.out.println(num); }
在做斐波那契数列时,不建议使用递归方法,调用了两次该方法,就是两个方法不停地递归,之后可能会有更多的递归,有点像一颗树。而且方法不停的重复计算数据,效率比较低下。就拿fib(1)和fib(2)来说就重复8次,如果求n= 40的fib,重复会更高。
建议使用循环去实现菲波那切数列
//循环实现fib public static int fib(int n) { if(n == 1 || n == 2){ return 1; } int f1 = 1; int f2 = 1; int fn = n; for(int i = 3; i <= n; ++i){ fn = f1 + f2; f1 = f2; f2 = fn; } return fn; } public static void main(String[] args) { int num = fib(6); System.out.println(num); }
汉诺塔
把n个盘子借助b从a移到c,现在我不会移动n个过去,那我先移动一个过去
//汉诺塔问题 //n代表盘子数,意思是把n个盘子从a柱子借助b柱子移动到c柱子 public static void hanno(int n,char A,char B,char C){ if(1 == n){ System.out.println(A+"-->"+C); return ; } hanno(n-1,A,C,B); System.out.println(A+"-->"+C); hanno(n-1,B,A,C); } public static void main(String[] args){ hanno(3,'A','B','C'); }
当一个盘子直接从a移到c,当n个盘子,那我就先从把n-1个盘子从a借助b移到c,再把剩下的一个从a移到c,最后把n-1个盘子从b借助a移到c。
刚开始学的时候,总想着去一步一步看看里面是怎么写的,就导致我当初学的时候很困难,但可以画图解释清楚,这次做了一些关于递归的题让我对递归有了另外一种认识:
1.找初始条件
2.理清楚原问题和拆分问题的关系从而建立递推关系找到一篇比较好的文章,看完后绝对有更深的理解:C语言 - 汉诺塔详解(超详细)_wh128341的博客-CSDN博客_c语言汉诺塔
青蛙跳台阶
一只青蛙可以一次跳 1 级台阶或一次跳 2 级台阶。试问跳n级台阶有多少种跳法。
这篇文章也是深刻讲解,可以去看看。
详解青蛙跳台阶的递归问题_Pinksatrfish的博客-CSDN博客_青蛙跳台阶递归//一只青蛙可以一次跳 1 级台阶或一次跳 2 级台阶 public static int step(int n){//跳n层台阶有step(n)种结果 if(1 == n){ return 1; } if(2 == n){ return 2;//每次跳一级或一次跳两级 } //其余的情况都可以用前两种推出来 //n层台阶的方法为: n-1层台阶的方法+ n-2 层台阶的方法。 int sum = step(n - 1) + step(n - 2); return sum; } public static void main(String[] args) { int sum = step(4); System.out.println(sum); }
刚开始做的时候大家都想着如何去把代码一步一步走下去,那就试着画多个方法图然后去理解,等做到斐波那契、汉诺塔时,大家就会想着将原问题拆为一个小问题+部分去解决