递归算法是很常用的算法思想。使用递归算法,往往可以简化代码编写,提高程序的可 读性。但是,不合适的递归往往导致程序的执行效率变低。
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:759057893@qq.com 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.递推算法思想
递推算法是很常用的算法思想,在数学计算等方面有着广泛的应用。递推算法适合有着明显公式规律的场合。
递推算法是一种理性思维模式的代表,其根据已有的数据和关系,逐步推导而得到结果。 递推算法的执行过程如下:
- 根据已知结果和关系,求解中间结果。
- 判定是否达到要求,如果没有达到,则继续根据已知结果和关系求解中间结果;如果满足要求,则表示寻找到一个正确的答案。
递推算法往往需要用户知道答案和问题之间的逻辑关系。在许多数学问题中,都有着明确的计算公式可以遵循,因此往往可以采用递推算法来实现。
2.1 递推算法实例
递推算法是基本的算法思想,常用于数学相关的场合。下面通过一个简单的数学例子来析递推算法的应用。
数学里面的斐波那契数列便是一个使用递推算法的经典例子。13世纪意大利数学家斐波那契的《算盘书》中记载了典型的兔子产仔问题,其大意如下: 如果一对两个月大的兔子以后每一个月都可以生一对小兔子,而一对新生的兔子出生两个月后才可以生小兔子。也就是说,1月份出生,3月份才可产仔。那么假定一年内没有发生兔子死亡事件,那么1年后共有多少对兔子呢?
package com.lyz.dataStructure.algorithm;
import java.util.Scanner;
/**
*@Author:759057893@qq.com 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;
}
}
}
改进的费波纳茨算法
public class Test8 {
public static int Fibonacci(int n) {
int a=1,b=1,c=0;
if(n<0){
return 0;
}else if(n==1||n==2){
return 1;
}else{
for (int i=3;i<=n;i++){
c=a+b;
b=a;
a=c;
}
return c;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
System.out.println(Fibonacci(n));
}
}
}
3.穷举算法基本思想
穷举算法的基本思想就是从所有可能的情况中搜索正确的答案,其执行步骤如下
(1) 对于一种可能的情况,计算其结果。
(2) 判断结果是否满足要求,如果不满足则执行第(1)步来搜索下一个可能的情况;如果满足要求,则表示寻找到一个正确的答案。
在使用穷举算法时,需要明确问题的答案的范围,这样才可以在指定范围内搜索答案。 指定范围之后,就可以使用循环语句和条件判断语句逐步验证候选答案的正确性,从而得到需要的正确答案.
3.1穷举算法实例
穷举算法是最基本的算法思想,下面通过一个简单的例子来分析穷举算法的应用。鸡兔同笼问题最早记载于1500年前的《孙子算经》,这是我国古代一个非常有名的问题。鸡兔同笼的原文如下:
今有鸡兔同笼,上有三十五头,下有九十四足,问鸡兔各几何?这个问题的大致意思是:在一个笼子里关着若干只鸡和若干只兔,从上面数共有35个头从下面数共有94只脚。问笼中鸡和兔的数量各是多少?
package com.lyz.dataStructure.algorithm;
import java.util.Scanner;
/**
*@Author:759057893@qq.com 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:759057893@qq.com Lyz
* @Date: ${Date} 14:42
* @Description:
**/
import java.util.Scanner;
/**
*@Author:759057893@qq.com 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;
}
}