递归算法分析

递归算法

引言: 你想买部新手机,于是你去问老爸要钱,老爸说:“找你老妈要去!”,然后你听了老爸的话,找到老妈,老妈说:“想要钱问你老爸要!”,于是你又听了老妈的话,结果你又去找老爸,无奈老爸的回答还和刚才一样,你又去找老妈,老妈的回答也还是刚才那样,然后。。。你就进入递归了!

基础知识

基本概念: 递归是指一个函数自己调用自己。利用数学表达式可表达为:f(x)= f(x-1)+1

递归算法一般要经历两个过程: 递推(归)和回溯。如下图。
递归算法分析_第1张图片
递归算法的基本设计法则:
1、基准情形:必须要有某个基准情形,它无需递归就能解出。
2、不断推进:对于需要求解的情形,每次递归调用都必须要使状况朝向一种基本情况推进。
3、设计法则:假设所有的递归调用都能运行。
4、合成效益发展:在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作(例如使用递归计算斐波那契数列)。

常见的经典递归算法

利用递归求阶乘

代码:

/*
 * 递归求阶乘
 */

public class Test{
     public static void main(String[] args) {
         System.out.println( jie(5) );
     }     
     public static long jie( long val ) {
         long a = 1;
         if(val == 1 ) {
              a=1;
         }else {
              a = jie(val-1)*val;
         }
         return a;
     }
}

执行结果: 120
基准情形: val =1
递归条件: val>1
递归过程: 假设求5!,我们为方法传进一个5,if语句判断此值不为1则执行下方的递推式(a = jieCheng(val-1)*val),可以看到在此处又调用了求阶乘的方法 jie(),此处求的是4的阶乘,在val为1之前没次递推一个单位的阶乘,直到val为1时递推结束然后开始回溯,可以参考上方的图片加以理解。

利用递归倒叙输出一个数

代码:

public class Test {
     public static void main(String[] args) {
         System.out.println(replace(123456));
     }     

     public static String replace(int n) {
         int a = n % 10;//可以和下一步合并为:String s = String.valueOf(n%10);
         String s = a + "";
         if(n/10 !=0) s = s + replace(n/10);
         return s;
     }
}

执行结果: 654321
基准情形: n/10 =0
递归条件: n/10 !=0
递归过程: 同上,需要注意的是s + replace(n/10)二者的位置,递推replace(n/10)得到的是当前数字的钱一项,要把它放在以有字符串(s)的后面,例当s=“6”时,其递推的replace(n/10)=“5”,即“6”+“5”。

利用递归将十进制转化一到十进制

代码:

/*
 * 编写一个函数“change(x,r)”,将十进制的数x转换成r(1mothed(x/r,r);
 */
public class JinZhiZhuanHuan {
     public static void main(String[] args) {
         System.out.println(change(127,2));
     }
     static String change(int x,int r) {
         int c=x%r;//%取余,/取没有余数的商。此步可以和下一步合并。
         String s = c + "";
         if(x/r != 0) {
              s = change(x/r,r)+s;
         }
         return s;
     }
}

执行结果: 1111111
基准情形: x/r =0
递归条件: x/r != 0
递归过程: 主要首先判断x/r是否为零,如果不为零进行递推,为零时表示结束,此时返回字符串s。

利用递归求斐波那契数列的某一项

代码:

public class Test {
     /*
      * 求某一项的斐波那契数列的值
      * 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584 .....
      */

     public static void main(String[] args) {
         System.out.println(febo(9));
     }
     public static long febo(int n) {
         long val = 0;
         if(n == 1 || n == 2) {
              val = 1;
         }else {
              val = febo(n-1)+febo(n-2);//一次进行两次递推(归),不提倡这种做法
         }
         return val;
     }
}

执行结果: 34
基准情形: n == 1 || n == 2
递归条件: n>2
递归过程: 略。

总结

递归算法其实没有那么难,从上面的实例可以看出关键在于找出基准情形和递归条件(二者通常是对立的),然后再分析程序执行是否有相似之处,一般确定这两个关键点就可以实现递归了。

递归的优劣

优: 使函数的定义和算法的描述简洁易理解。
缺: 函数递归的过程需要系统堆栈,所以空间消耗要比非递归代码要大很多。而且,如果递归深度太大,可能造成栈溢出。

递归的应用

递归在其他算法中也得到了广泛的使用,在深搜、并查集、树的遍历等过程中均有递归的影子,递归简单易懂的算法描述给其他算法再来了很大的便利,使其他算法也变得简单易懂。

递归算法的实现类似于多个算法的嵌套调用,只是调用算法和被调用算法是同一个算法,因此,和每次调用相关的概念是递归调用算法的调用层次,如果调用一个递归算法的主函数为第0层时,那么从主算法调用递归算法为进入第一层调用,从第i层递归调用本算法为进入第i+1层调用。反之,退出第i层调用,则进入第i-1层调用。

参考:
[1] 数据结构与算法分析Java语言描述(第三版)
[2] 程序设计竞赛基本算法—递归http://www.saikr.com/t/2112

你可能感兴趣的:(递归算法分析)