思考:
-找规律
-先一直到底在进行回归,方法未结束
-问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解
-从下往上
-类似于入栈出栈
-分解的小问题的解构成了大问题的解,小问题不解决大问题没法解决
一、基本概念
-递归算法是一种直接或者间接调用自身函数或者方法的算法
-实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解
-我们就可以利用大道至简的思想,把一个大的复杂的问题层层转换为一个小的和原问题相似的问题来求解的这样一种策略
-递归趋势从下往上的进行思维
递归算法解决问题的特点:
1)递归就是方法里调用自身。
2)在使用递增归策略时,必须有一个明确的递归结束条件,称为递归出口。
3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。
递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。
一定要把握住出口,也就是做递归算法必须要有一个明确的递归结束条件。
二、程序示例
①斐波纳契数列(Fibonacci Sequence)
问题描述:求解Fibonacci数列的第n个位置的值?(斐波纳契数列(Fibonacci Sequence),又称黄金分割数列,
指的是这样一个数列:1、1、2、3、5、8、13、21、……在数学上,斐波纳契数列以如下被以递归的方法定义:
F1=1,F2=1,Fn=F(n-1)+F(n-2)(n>2,n∈N*))。
public static void main(String[] args){
int count;
int n;
System.out.print("请输入n的值:");
Scanner scan = new Scanner(System.in);
count=scan.nextInt();
n = circule(count);
System.out.println(n);
}
public static int circule(int n)
{
if(n==1||n==2){
return 1;
}
else{
return circule(n-1)+circule(n-2);
}
}
}
风险:我们在f4里面已经计算了f2,可是f3里有同样计算了f2,以此类推那些冗余的工作,在数值比较小的情况下,
计算机还是可以接受的。但是,当求解的数值比较大,它是成指数级增长的,所以不要再递归中做重复的工作。
②n的阶乘
public static void main(String[] args){
int count;
int n;
System.out.print("请输入n的值:");
Scanner scan = new Scanner(System.in);
count=scan.nextInt();
n = circule(count);
System.out.println(n);
}
public static int circule(int n){
if(n==1){
return 1;
}
else{
return circule(n-1)*n;
}
}
③列出某个目录下所有的子目录和文件
public static void main(String[] args){
int count;
int n;
System.out.print("请输入n的值:");
Scanner scan = new Scanner(System.in);
count=scan.nextInt();
n = circule(count);
System.out.println(n);
}
public static int circule(int n){
if(n==1){
return 1;
}
else{
return circule(n-1)*n;
}
}
③汉诺塔问题
该问题可以分解成以下子问题:
第一步:将n-1个盘子从A柱移动至B柱(借助C柱为过渡柱)
第二步:将A柱底下最大的盘子移动至C柱
第三步:将B柱的n-1个盘子移至C柱(借助A柱为过渡
#include <stdio.h>
#include <stdlib.h>
int i; //记录步数
//i表示进行到的步数,将编号为n的盘子由from柱移动到to柱(目标柱)
void move(int n,char from,char to){
printf("第%d步:将%d号盘子%c---->%c\n",i++,n,from,to);
}
//汉诺塔递归函数
//n表示要将多少个"圆盘"从起始柱子移动至目标柱子
//start_pos表示起始柱子,tran_pos表示过渡柱子,end_pos表示目标柱子
void Hanio(int n,char start_pos,char tran_pos,char end_pos){
if(n==1){ //很明显,当n==1的时候,我们只需要直接将圆盘从起始柱子移至目标柱子即可.
move(n,start_pos,end_pos);
}
else{
Hanio(n-1,start_pos,end_pos,tran_pos); //递归处理,一开始的时候,先将n-1个盘子移至过渡柱上
move(n,start_pos,end_pos); //然后再将底下的大盘子直接移至目标柱子即可
Hanio(n-1,tran_pos,start_pos,end_pos); //然后重复以上步骤,递归处理放在过渡柱上的n-1个盘子 //此时借助原来的起始柱作为过渡柱(因为起始柱已经空了)
}
}
int main(){
int n;
while(scanf("%d",&n)==1&&n){
i = 1; //全局变量赋初始值
Hanio(n,'1','2','3');
printf("最后总的步数为%d\n",i-1);
}
return 0;
}
三、递归算法转换成非递归算法
-将递归算法转换为非递归算法有两种方法,一种是直接求值,不需要回溯;另一种是不能直接求值,
需要回溯。前者使用一些变量保存中间结果,称为直接转换法;后者使用栈保存中间结果,称为间接转换法
-1. 直接转换法
直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后。例如求阶乘的递归算法:
public long fact(int n)
{
if (n==0) return 1;
else return n*fact(n-1);
}
当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,所以
,不必利用栈来保存返回信息。对于尾递归形式的递归算法,可以利用循环结构来替代。例如求阶乘的递归算法
可以写成如下循环结构的非递归算法:
public long fact(int n)
{
int s=0;
for (int i=1; i
s=s*i; //用s保存中间结果
return s;
}