1.递归概述
对递归最恰当的比喻,就是查词典。我们查词典的过程,本身就是递归。想象用一本纯
英文词典查单词,要查某一个单词的意思,翻到这个单词时,看解释,发现解释中有一个单
词不认识,所以,无法明白这个要查的单词是什么意思;这时,再用这本词典(函数本身)
查那个不认识的单词,又发现查的第2个单词的解释中又有一个单词不认识,那么,又再用
这本词典查第3个不认识的单词,这样,一个一个查下去,直到解释中所有单词都认识,这
样就到底了,就明白了最后一个单词是什么意思,然后一层一层倒回来,就知道我最初想查 的第1个单词是什么意思了,问题就解决了。
递归
- 递归就是方法里调用自身
- 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口
- 递归算法解题通常显得很简洁,但递归算法解题的运行效率较低
- 在递归调用的过程中,系统为每一层的返回点、局部变量等开辟了栈来存储。递
归次数过多容易造成栈溢出等
具体地说,如果递归函数调用自己,则被调用的函数也将调用自己,这将无限循环下
去,除非代码里有终止调用的语句。通常的方法是将递归调用放在if语句中
void function(arg1){
statements1
if(condition){ //condition为false,则终止调用,递归链断掉
function(arg2)
}
statements2
}
只要if语句为true,每个function()调用都将先执行statements1,然后再调用
function(),而不会执行statement2。当前调用结束后,程序控制权将返回给调用它的
function(),而该function()将继续执行statement2部分,然后结束,并将控制权返回给前
一个调用,以此类推。
写出递归函数也就是要处理好递归的3个主要的点
- 出口条件,即递归“什么时候结束”,这个通常在递归函数的开始就写好
- 如何由"情况n" 变化到"情况n+1",也就是非出口情况,也就是一般情况
——"正在"递归中的情况 - 初始条件,也就是这个递归调用以什么样的初始条件开始
递归的基本思想
是广义地把规模大的问题转化为规模小的相似的子问题或者相似的子问题集合来解决。
广义针对规模的,规模的缩小具体可以是指递归函数的参数,也可以是其参数之一。相似是
指解决大问题的方法和解决小问题的方法往往是同一个方法,还可以是指解决子问题集的各
子问题的方法是同一个方法。解决大问题的方法可以是由解决次规模问题的方法和解决剩余 部分的方法组成,也可以是由一系列解决次规模问题的方法组成。
2.递归应用
- 求和1+2+3+…+(n-1)+n
class Demo{
public static void main(String[] args){
int n=100;
int sum=getSum(n);
System.out.println(sum);
}
public static int getSum(int n){
if(n==1){ //终止条件
return 1;
}
return getSum(n1)+n; //递归调用
}
}
分析:
递归不能解决深度过大的问题
但凡是迭代的问题,都可以用递归解决,但是不代表递归的问题可以用迭代解决。
斐波那契数列
斐波那契数,亦称之为斐波那契数列(意大利语: Successione di
Fibonacci),又称黄金分割数列、费波那西数列、费波拿契数、费氏数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……在数学上,斐波那契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列由
0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加。
class Demo{
public static void main(String[] args){
for(int i=1;i<=20;i++){
int num=fibo(i);
System.out.println(num);
}
}
public static int fibo(int i){
if(i==1||i==2){
return 1;
}
return fibo(i1)+fibo(i2);
}
}
分析:
迭代方法实现
class Demo{
public static void main(String[] args){
for(int i=1;i<=20;i++){
int num=fibo(i);
System.out.println(num);
}
}
public static int fibo(int i){
if(i==1||i==2){
return 1;
}
long a=1;
long b=1;
for(int i=0;i<n-1;i++){
c=a+b;
a=b;
b=c;
}
}
}
求n的阶乘 n!
class Demo{
public static void main(String[] args){
int n=10;
int num=get(n);
System.out.println(num);
}
public static int get(int n){
if(n==0||n==1){ //判断种植条件
return 1;
}
return get(n1)*n; //递归调用
}
}
汉诺塔问题
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
- 代码实现:
private static void hano(String from, String mid, String to, int n) {
if(n==1){ //函数终止条件
System.out.println(from+"->"+to);
}else{
hano(from,to,mid,n-1);
System.out.println(from+"->"+to);
hano(mid, from, to, n-1);
}
}
- 用栈实现代码
private static void demo1() {
int N=30;
LinkedStack stackX=new LinkedStack<>();
for(int i=N;i>=1;i--){
stackX.push(i);
}
LinkedStack stackY=new LinkedStack<>();
LinkedStack stackZ=new LinkedStack<>();
move(stackX,stackY,stackY,N);
}
private static void move(LinkedStack x, LinkedStack y,
LinkedStack z, int Level) {
//x 源盘 y 借助盘 z 目的盘
if(Level==1){
z.push(x.pop());
}else{
move(x,z,y,Level-1);
z.push(x.pop());
move(y, x, z, Level-1);
}