数据结构——递归算法、递推算法、穷举算法、分治算法

递归算法是很常用的算法思想。使用递归算法,往往可以简化代码编写,提高程序的可 读性。但是,不合适的递归往往导致程序的执行效率变低。

1、递归算法基本思想

递归算法即在程序中不断反复调用自身来达到求解问题的方法。此处的重点是调用自身, 这就要求待求解的问题能够分解为相同问题的一个子问题。这样,通过多次递归调用,便可 以完成求解。

递归调用是一个方法在其方法体内调用其自身的方法调用方式。这种方法也称为“递归 方法”。在递归方法中,主调方法又是被调方法。执行递归方法将反复调用其自身。每调用一 次就进入新的一层。

方法的递归调用分两种情况:直接递归和间接递归。

•直接递归,即在方法中调用方法本身。

•间接递归,即间接地调用一个方法,如func a调用func_b, func b又调用func a。间 接递归用得不多。

编写递归方法时,必须使用if语句强制方法在未执行递归调用前返回。如果不这样做, 在调用方法后,它将永远不会返回。这是一个很容易犯的错误。

了解了递归方法的设计方法和工作原理后,即可对递归的优缺点进行以下总结。

递归的优点:序代码更简洁清晰,可读性更好。有的算法用递归表示 要比用循环表示简洁精练,而且某些问题,特别是与人工智能有关的问题,更适宜用递归方 法,如八皇后问题、汉诺塔问题等。有的算法,用递归能实现,而用循环却不一定能实现。

递归的缺点:大部分递归例程没有明显地减少代码规模和节省内存空间。递归形式比非 递归形式运行速度要慢一些。这是因为附加的方法调用增加了时间开销,例如需要执行一系 列的压栈出栈等操作。但在许多情况下,速度的差别不太明显。如果递归层次太深,还可能 导致堆栈溢出

1.1递归算法实例

递归算法常用于一些数学计算,或者有明显的递推性质的问题。理解递归最常用的一个 例子是编写程序求阶乘问题。

1.递归算法

所谓阶乘,就是从1到指定数之间的所有自然数相乘的结果,"的阶乘为:

n!=n*(n-1)*(n-2)*...*2*1

而对于(n-1)!,则有如下表达式:

(n-1)!=(n-1)*(n-2)*...*2*1

从上述两个表达式可以看到阶乘具有明显的递推性质,即符合如下递推公式:

n!=n*(n-1)!

使用代码展示:

package com.lyz.dataStructure.algorithm;

import java.util.Scanner;

/**
 *@Author:[email protected] Lyz
 *@Date: 2019/4/1 15:27
 *@Description:
 **/
public class RecursionDemo {
    public static void main(String[] args) {
        int i;
        Scanner input = new Scanner(System.in);
        System.out.println("请输入要求阶乘的一个整数");
         i = input.nextInt();
        RecursionDemo rd = new RecursionDemo();
        System.out.println(i+"的阶乘结果为:"+rd.fact(i));
    }


    public long fact(int n){
        if(n<=1){
            return  1;
        }else{
            return fact(n-1)*n;
        }
    }
}

2.递推算法思想

递推算法是很常用的算法思想,在数学计算等方面有着广泛的应用。递推算法适合有着明显公式规律的场合。

递推算法是一种理性思维模式的代表,其根据已有的数据和关系,逐步推导而得到结果。 递推算法的执行过程如下:

  1. 根据已知结果和关系,求解中间结果。
  2. 判定是否达到要求,如果没有达到,则继续根据已知结果和关系求解中间结果;如果满足要求,则表示寻找到一个正确的答案。

递推算法往往需要用户知道答案和问题之间的逻辑关系。在许多数学问题中,都有着明确的计算公式可以遵循,因此往往可以采用递推算法来实现。

2.1 递推算法实例

递推算法是基本的算法思想,常用于数学相关的场合。下面通过一个简单的数学例子来析递推算法的应用。

数学里面的斐波那契数列便是一个使用递推算法的经典例子。13世纪意大利数学家斐波那契的《算盘书》中记载了典型的兔子产仔问题,其大意如下: 如果一对两个月大的兔子以后每一个月都可以生一对小兔子,而一对新生的兔子出生两个月后才可以生小兔子。也就是说,1月份出生,3月份才可产仔。那么假定一年内没有发生兔子死亡事件,那么1年后共有多少对兔子呢?

package com.lyz.dataStructure.algorithm;

import java.util.Scanner;

/**
 *@Author:[email protected] Lyz
 *@Date: 2019/4/2 11:04
 *@Description:
 **/

/*
递推法:
如果一对两个月大的兔子以后每一个月都可以生一对小兔子,
而一对新生的兔子出生两个月后才可以生小兔子。也就是说,
1月份出生,3月份才可产仔。那么假定一年内没有发生兔子死亡事件,那么2年后共有多少对兔子?
 */
public class Recurrence {
    public static void main(String[] args) {
        Recurrence r = new Recurrence();
        Scanner input = new Scanner(System.in);
        System.out.println("递推算法求解兔子产仔问题");
        System.out.println("请输入月份:");
        int n = input.nextInt();
        int num = r.fibonacci(n);
        if(n<0) {
            System.out.println("输入时间不能小于等于0");
        }else{
            System.out.println("经过"+n+"月时间的繁殖,总共繁殖出"+num+"对兔子!");
        }

    }

    public int fibonacci(int n){
        int t1,t2;
        if(n<0){
            return -1;
        } else if(n==1||n==2){
            return 1;
        }else{
            t1 = fibonacci(n-1);
            t2 = fibonacci(n-2);  //递归调用
            return t1+t2;
        }
    }
}

3.穷举算法基本思想

穷举算法的基本思想就是从所有可能的情况中搜索正确的答案,其执行步骤如下

(1) 对于一种可能的情况,计算其结果。

(2) 判断结果是否满足要求,如果不满足则执行第(1)步来搜索下一个可能的情况;如果满足要求,则表示寻找到一个正确的答案。

在使用穷举算法时,需要明确问题的答案的范围,这样才可以在指定范围内搜索答案。 指定范围之后,就可以使用循环语句和条件判断语句逐步验证候选答案的正确性,从而得到需要的正确答案.

3.1穷举算法实例

穷举算法是最基本的算法思想,下面通过一个简单的例子来分析穷举算法的应用。鸡兔同笼问题最早记载于1500年前的《孙子算经》,这是我国古代一个非常有名的问题。鸡兔同笼的原文如下:

今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?这个问题的大致意思是:在一个笼子里关着若干只鸡和若干只兔,从上面数共有35个头从下面数共有94只脚。问笼中鸡和兔的数量各是多少?

package com.lyz.dataStructure.algorithm;

import java.util.Scanner;

/**
 *@Author:[email protected] Lyz
 *@Date: 2019/4/2 10:36
 *@Description:
 **/

/*
今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?
 */
public class Exhaust {

    static int chichen,habbit; //鸡,兔

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        Exhaust e = new Exhaust();
        System.out.println("穷举法求解鸡兔同笼问题");
        int head,foot,re;
        System.out.println("请输入头数:");
        head= input.nextInt();
        System.out.println("请输入脚数:");
        foot= input.nextInt();
        re = e.qiongju(head,foot);
        if(re==1){
            System.out.println("鸡有"+chichen+"只,兔有"+habbit+"只");
        }    else{
            System.out.println("无法求解");
        }
    }


    public int qiongju(int head,int foot){
            int i,j,re=0;
            for( i =0;i<=head;i++){
                j=head-i;
                if(2*i+4*j==foot){
                    re =1;
                    chichen = i;
                    habbit = j;

                }
            }
            return  re;
    }
}

4.分治算法思想

分治算法是一种化繁为简的算法思想。分治算法往往应用于计算步骤比较复杂的问题,通过将问题简化而逐步得到结果。

分治算法的基本思想是将一个计算复杂的问题分为规模较小、计算简单的小问题求解, 然后综合各个小问题,得到最终问题的答案。分治算法的执行过程如下:

(1) 对于一个规模为N的问题,若该问题比较容易解决(比如规模N较小),则直接解决;否则执行下面的步骤。

(2) 将该问题分解为M个规模较小的子问题,这些子问题互相独立,并且与原问题形式相同。

(3) 递归地解这些子问题。

(4) 然后,将各子问题的解合并得到原问题的解。

使用分治算法需要待求解问题能够转化为若干个小规模的相同问题,通过逐步划分,能够达到一个易于求解的阶段而直接进行求解。然后,程序中可以使用递归算法来进行求解。

4.1分治算法实例

下面通过一个例子来分析分治算法的应用。一个袋子里有30个硬币,其中一枚是假币, 并且假币和真币一模一样,肉眼很难分辨,目前只知道假币比真币的重量轻一点。请问,如何区分出假币呢?

package com.lyz.dataStructure.algorithm;/**
 * @Author:[email protected] Lyz
 * @Date: ${Date} 14:42
 * @Description:
 **/

import java.util.Scanner;

/**
 *@Author:[email protected] Lyz
 *@Date: 2019/4/2 14:42
 *@Description:
 **/
public class Coin {

    static  final int MAXNUM=30;

    public static void main(String[] args) {

        int[] coin = new int[MAXNUM];
        int i,n;
        int weizhi;
        System.out.println("分治算法求解假币问题");
        System.out.println("请输入硬币总的个数");
        Scanner input = new Scanner(System.in);
        n=input.nextInt();          //硬币总的个数
        System.out.println("请输入硬币的真假:");
        for(i=0;i<n;i++){
            coin[i] = input.nextInt();  //输入硬币的真假
        }
        weizhi = FalseCoin(coin,0,n-1);
        System.out.println("在上述"+MAXNUM+"个硬币中,第"+weizhi+"个硬币是假的!");
    }


   static int FalseCoin(int coin[],int low,int high){
        int i,sum1,sum2,sum3;
        int re=0;
        sum1 =sum2=sum3=0;
        if(low+1==high){
            if(coin[low]<coin[high]){
                re=low+1;
                return re;
            }else{
                re=high+1;
                return re;
            }
        }
       if ((high-low+1)%2==0) {  //n是偶数
            for(i=low;i<=low+(high-low)/2;i++){
                sum1=sum1+coin[i];    //前半段和
            }
            for(i=low+(high-low)/2+1;i<=high;i++){
                sum2=sum2+coin[i];
            }
            if(sum1>sum2){
                re=FalseCoin(coin,low+(high-low)/2+1,high);
                return re;
            }else if(sum1 <sum2){
                re=FalseCoin(coin,low,low+(high-low)/2);
                return  re;
            }else{

            }
       }else{
          for(i=low;i<=low+(high-low)/2;i++){
              sum1=sum1+coin[i];    //前半段和
          }
          for(i=low+(high-low)/2+1;i<=high;i++){
              sum2=sum2+coin[i];
          }
          sum3=coin[low+(high-low)/2];
           if (sum1>sum2) {
               re=FalseCoin(coin,low+(high-low)/2+1,high);
               return re;
           }else if (sum1<sum2){
               re =FalseCoin(coin,low,low+(high-low)/2-1);
               return re;
           }else{

           }
           if(sum1+sum3==sum2+sum3){
               re=low+(high-low)/2+1;
               return re;
           }
       }
       return re;
       }

    }

猜你喜欢

转载自blog.csdn.net/weixin_38201936/article/details/88948601