递归算法

递归

 引用一个有趣的例子:在正式介绍递归之前,我们首先引用知乎用户李继刚(https://www.zhihu.com/question/20507130/answer/15551917)对递归和循环的生动解释:

递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。

循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。

  • 定义:
     递归的过程是指函数调用自身的过程。一般用于一些算法,例如求一个数的阶乘、计算斐波那契(Fibonacci)数列、汉诺塔、杨辉三角的存取、字符串回文判断、字符串全排列、二分查找、树的深度求解在内的八个经典问题。
  • 精髓:
     举例1:基础的求一个数的阶乘问题入手:
     (1)for循环实现:
public class Testdome1026 {
public static  int factorial(int n){
       int j =1;
       for(int i=1;i <= n;i++){
           j *= i;
       }
       return j;
   }
    public static void main(String[] args) {
      System.out.println(factorial(5));
    }
 }

 (2)递归实现:

public class Testdome1026 {
 public  static int factorial2(int n){
       int j = 1;
       if (n == 1) {
          j = 1;
          return  j;
       }else {
           j = factorial2(n-1) * n;
           return j;
       }
   }
    public static void main(String[] args) {
      System.out.println(factorial(5));
    }
}
120

 图解分析递归过程:
在这里插入图片描述
 举例2:经典的汉诺塔问题:
 有三根柱子A,B,C,A柱子上有N个盘子,从小到大依次叠放,要求把A上的盘子都移到C上,B可以作为临时存放,移动的时候必须始终遵循小盘子在大盘子上面,且每次只能移动一个盘子,求其算法。
递归实现:

public static void move(char pos1,char pos2){
       System.out.println(pos1+"==>"+pos2+" ");
    }//盘子移动路径
    public static void hanio(int n,char pos1,char pos2,char pos3){
       if (n == 1){
           move(pos1,pos3);
       }else{
           hanio(n-1,pos1,pos3,pos2);
           move(pos1,pos3);
           hanio(n-1,pos2,pos1,pos3);//递归调用自身函数
       }
    }
    public static void main(String[] args) {
        hanio(2,'A','B','C');
        System.out.println();
        hanio(3,'A','B','C');
    }
}
A==>B 
A==>C 
B==>C 

A==>C 
A==>B 
C==>B 
A==>C 
B==>A 
B==>C 
A==>C 

总结:
递归是静中有动,有去有回。
循环是动静如一,有去无回。

  • 循环是进入问题会有一个特定的循环体,不断地进入循环条件,直至输出所要的结果,走出问题。
  • 递归就是有去(递去)有回(归来)。
    递去:递归问题可以分成若干个很小的问题,从若干个小问题可以有相同的方法解决。
    归来:这要求这些问题不断从大到小,从近及远的过程中,会有一个终点,一个临界点,一个baseline,一个你到了那个点就不用再往更小,更远的地方走下去的点,然后从那个点开始,原路返回到原点。
  • 递归要素
  1. 明确递归终止条件;(趋近于某个临界值)

  2. 给出递归终止时的处理办法;

  3. 提取重复的逻辑,缩小问题规模。

递归算法模型
 设计一个递归过程时,必须至少测试一个可以终止此递归的条件,并且必须对在合理的递归调用次数内不满足此类条件的情况下进行处理。

  • 模型一: 在递去的过程中解决问题
function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题
        solve;                // 递去
        recursion(小规模);     // 递到最深处后,不断地归来
    }
}

* 模型二: 在归来的过程中解决问题

function recursion(大规模){
    if (end_condition){      // 明确的递归终止条件
        end;   // 简单情景
    }else{            // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题
        recursion(小规模);     // 递去
        solve;                // 归来
    }
}

* 举例3: 斐波那契数列求前n项和:
(1)for循环实现:

public class Fibonacci {
    public static  int  fiboncic(int n){
        int f1 = 1;
        int f2 = 1;
        int f3 = 1;
        for (int i = 3;i < n;i  ++){
            f3 = f1 + f2;
            f1 = f2;
            f2 = f3;
        }
        return f3;              
    }
    public static void main(String[] args) {
         System.out.println(40);
    }

(2)递归实现:

public class Main {
    public static int Fob(int n){
        if (n < 0) {   //明确终止递归的条件
            return -1;
        }//终止递归
        if ( n == 1 || n == 2) {
            return 1;
        }
        return Fob(n-1)+Fob(n-2);//递归调用
    }
    public static void main(String[] args) {
        int sum = 0;
        for (int i = 1; i <= 5 ; i++) { //循环次数40,即前40项
            sum += Fob(i);//求和
        }
        System.out.println(sum);//打印并输出
    }
}
  • 递归的优缺点:
  • 优:代码简单,代码少,
  • 缺点:递归的话函数调用是有开销的,而且递归的次数受堆栈大小的限制。 而且,如果递归深度太大,可能系统撑不住。或者说,函数过程在每次调用它自身时,都会占用更多的内存空间以保存其局部变量的附加副本。如果这个进程无限持续下去,最终会导致StackOverflowError错误。
    递归和循环之间的选择:
  • 循环方法比递归方法快, 因为循环避免了一系列函数调用和返回中所涉及到的参数传递和返回值的额外开销。
    一般情况下, 当循环方法比较容易找到时, 你应该避免使用递归。这在问题可以按照一个递推关系式来描述时, 是时常遇到的, 比如阶乘问题就是这种情况。 反过来, 当很难建立一个循环方法时, 递归就是很好的方法。实际上, 在某些情形下, 递归方法总是显而易见的, 而循环方法却相当难找到。当某些问题的底层数据结构本身就是递归时, 则递归也就是最好的方法了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值