递归算法思想

来源:互联网 发布:宁夏网络干部培训 编辑:程序博客网 时间:2024/05/17 05:03

什么是递归,讲一个故事:从前有座山,山上有座庙,庙里有个老和尚,老和尚给小和尚讲故事。讲的什么故事呢?故事是这样的:山上有座庙,庙里有个老和尚,老和尚给小和尚讲故事。。。


递归算法是在程序中不断反复调用自身的方法从而解决特定问题的一种思想,重点是对自身的调用,将求解的问题分解为相同问题的一个子问题,通过多次递归调用,以完成求解。

递归方法中,该方法既是主调方法又是被调方法,反复调用其自身,没调用一次进入新的一层。简单说,递归就是自己调用自己。


递归分为直接递归和间接递归两种。

直接递归:在方法种调用方法本身。

间接递归:在方法体中间接的调用一个方法


递归方法的优点:

使程序代码更加简洁、清晰,提高代码的可读性。

递归方法的缺点:

使程序的运行速度变慢,压栈、出栈等操作增加了算法的时间复杂度和空间复杂度,递归层次过深,还易导致栈溢出等问题。


递归经典例题:

汉诺塔问题
目标:从A到C
调用方法直接用方法名调用,注意传入的参数个数和类型相同

public class Hannuota {    static int i=0;public static void main(String[] args) {hanoi(3,"A柱","B柱","C柱");System.out.print("循环的次数"+i);}   static void hanoi(int n,String a,String b,String c)//n表示n个盘子,abc三根柱子    {    if(n==1)    //一个盘子的情况    {    i++;    System.out.println(a+"--->"+c);    }else{    //n个盘子    hanoi(n-1,a,c,b);//将n-1个盘子从a借助c移动到b上    hanoi(1,a,b,c);    hanoi(n-1,b,a,c);    }    }    }


一、任何循环都可以重写为递归的形式。

将循环改为递归,重要的是发现逻辑的“相似性”,当然不能忘记递归的“出口”。

例1:循环打印0-9public class Digui_1 {public static void main(String[]args){for(int i=0;i<10;i++){System.out.println(i);}}}    (1)递归打印0-9public class Digui_1 {public static void count(int n){if(n>0){count(n-1);}System.out.println(n);}public static void main(String[]args){count(9);}}结果:0123456789  (2)递归打印0-9public class digui_1 {public static void count(int begin,int end){if(begin>end){return;}System.out.println(begin);count(begin+1,end);}public static void main(String[] args) {count(0,9);}}

二、构造相似性
递归类似于“踢皮球”,重点是寻找递归方法,即寻找相似性。如果没有明显的“相似性”,需主动构造,不能相似的原因可能是缺少参数。
例2:循环打印数组累加求和public class Digui2 {public static int addAll(int a[]){int x=0;for(int i=0;i<a.length;i++){x+=a[i];}return x;}public static void main(String[]args){int a[]={2,5,3,9,12,7};int sum=addAll(a);System.out.println(sum);}}(1)递归打印数组之和public class Digui2 {public static int count(int a[],int begin){//求a数组中,从begin元素到结束的元素和if(begin==a.length){//递归出口return 0;}int x=count(a, begin+1);return x+a[begin];}public static void main(String[]args){int a[]={2,5,3,9,12,7};int sum=count(a, 0);//从第0项开始累加System.out.println(sum);}}
例3:比较字符串内容public class Digui3 {public static boolean isSameString(String s1,String s2){return s1.equals(s2);}public static void main(String[]args){System.out.println(isSameString("abc", "abcd"));}}结果:false(1)递归比较字符串内容是否相等public class Digui3 {public static boolean count(String s1,String  s2){if(s1.length()!=s2.length()){//判断字符串长度return false;}if(s1.length()==0||s2.length()==0){//递归出口return true;}if(s1.charAt(0)!=s2.charAt(0)){//判断首字母return false;}return count(s1.substring(1), s2.substring(1));//递归,截取字符串}public static void main(String[]args){System.out.println(count("abc", "abcd"));}}



三、递归调用
递归调用仅仅是被调函数恰为主调函数。
注意每次调用的层次不同。
注意每次分配形参并非同一个变量。
注意返回的次序。


要点;先找相似性,写出递归的主体(寻找参数变化),再写出口条件(多为if语句)。


例1:在n个球中,任意取出m个球(不放回),求有多少种不同的取法。public class Digui4 {public static  int count(int n,int m){if(n<m){return 0;}//递归的出口if(n==m){return 1;};if(m==0){return 1;}//构造递归:假定将其中一个球标记,分成两类,取出包含标记球和取出不包含标记球return count(n-1,m-1)+count(n-1,m);}public static void main(String[]args){int flag=count(10,3);System.out.println(flag);}}结果:120例2:求n个元素的全排列public class Digui5 {//k为当前的交换位置,将k位与后面的每一位进行交换public static void count(char data[],int k){if(k==data.length){for(int i=0;i<data.length;i++){System.out.print(data[i]+" ");}System.out.println();}for(int i=k;i<data.length;i++){//递归的出口char t=data[k];//交换位置data[k]=data[i];data[i]=t;count(data, k+1);t=data[k];//回溯data[k]=data[i];data[i]=t;}}public static void main(String[]args){char data[]="ABC".toCharArray();count(data,0);}}结果:A B C A C B B A C B C A C B A C A B 例3:求两个串的最大公共子序列的长度public class Digui6 {public static int count(String s1,String s2){if(s1.length()==0||s2.length()==0){return 0;}if(s1.charAt(0)==s2.charAt(0)){return count(s1.substring(1),s2.substring(1))+1;}else{return Math.max(count(s1.substring(1), s2),count(s1, s2.substring(1)));}}public static void main(String[]args){int k=count("abc","xbacd");System.out.println(k);}}例一。反转串 cba为abc的反转串public class Digui7 {public static String count(String s){if(s.length()<=1){return s;}return count(s.substring(1))+s.charAt(0);}public static void main(String[]args){System.out.println(count("abc"));}}例二。杨辉三角形,计算第m层第n个系数的方法(m,n从0开始)11  11  2  11  3  3  11  4  6  4  11  5  10 10 5  1public class Digui8 {public static int count(int m,int n){if(n==0){return 1;}//第一列元素if(m==n){return 1;}//每行最后一个元素return count(m-1, n)+count(m-1, n-1);}public static void main(String[]args){int level=5;for(int i=0;i<=level;i++){System.out.print(count(level,i)+" ");}System.out.println();}}例三。计算m个A,n个B可以组成多少种排列?public class Digui9 {public static int count(int m,int n){//以第一个元素为A或者B划分为两类if(m==0||n==0){return 1;}return count(m-1, n)+count(m, n-1);}public static void main(String[]args){System.out.println(count(3,2));}}例四。整数的划分问题  对于给定正整数n,打印所有划分public class Digui_01 {//对n进行加法划分,a为缓冲数组,k为当前的位置public static void count(int n,int a[],int k){if(n<=0){for(int i=0;i<k;i++){System.out.print(a[i]+" ");}System.out.println();return;}for(int i=n;i>0;i--){//每列的一个字母if(k>0&&i>a[k-1]){continue;}//保证前一项大于后一项a[k]=i;count(n-i,a,k+1);}}public static void main(String[]args){int a[]=new int[1000];count(6,a,0);}}结果:6 5 1 4 2 4 1 1 3 3 3 2 1 3 1 1 1 2 2 2 2 2 1 1 2 1 1 1 1 1 1 1 1 1 1 

总结:递归的基本思想是把规模大的问题转化为规模小的相似的子问题来解决。在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况。另外这个解决问题的函数必须有明显的结束条件,这样就不会产生无限递归的情况了。

关于递归推荐两部电影《盗梦空间》《奇异博士》。





1 0