介绍递归的定义、特点和基本原理;在此基础上给出一系列性质,对递归问题进行分类;通过经典问题讲解递归算法的实现
定义
递归就是程序调用自身的编程技巧;
特点
1)递归就是在过程或者函数里面调用自身;
2)在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口;
3)在递归调用过程中,系统位每层的返回点、局部量开辟栈来存储。递归次数越多,容易造成栈溢出等。
经典问题
1.汉诺塔问题
问题描述:有3个柱子,A,B和C,A上有64个盘子,从上向下盘子越来越大,想要把A上的盘子移到C上;移动时一次只能移动一个并且只能小盘子压大盘子,欲使A上盘子移动到C上,最少多少次?
递归往下的过程是:
第一个人对第二个人说:
1)你把前63个盘子移到第二个柱子上;
2)然后我把第64个盘子移动到第三个柱子上;
3)你把前63个盘子移动到第三个柱子上;
第二个人对第三个人说:
1)你把前62个盘子移到第三个柱子上;
2)然后我把第63个盘子移动到第二个柱子上;
3)你把前62个盘子移动到第二个柱子上;
依次往下到只有一个盘子。
- 整体分析就是,把n个盘子从A移到C:
(1)把柱A上(n-1)个盘子借助C移到B
(2)把柱A上第n个盘子移到C
(3)把柱B上(n-1)个盘子借助A移到C
代码实现:
public class A {
public static void main(String[] args) {
// TODO Auto-generated method stub
A a = new A();
a.hanoi(10,'a','b','c');
}
public void hanoi(int n,char a,char b,char c){
if(n == 1){
move(1,a,c);
}else{
hanoi(n-1, a,c,b);
move(n,a,c);
hanoi(n-1, b,a,c);
}
}
public void move(int i, char a, char c) {
// TODO Auto-generated method stub
System.out.println(new String(i + "move from" + a + "to" + c));
}
}
2.半数集问题
问题描述:(1)n是set中的元素;(2)在n左边添加一个自然数,该自然数不能超过最近添加数的一半;(3)按此规律,直到不能添加数为止
思路:以8为例,第一次可以添加18,28,38,48;第二次添加128,138,238,148,248;第四次添加1248
代码实现:
public int halfnum(int num){
int ant = 1;
if(num > 1){
for(int i=1;i<=num/2;i++)
ant += halfnum(i);
}
return ant;
}
3.分书问题
问题描述:有编号1....n的n本书,准备分给n个人,每个人阅读兴趣用一个二维数组加以描述;1代表喜欢;0代表不喜欢,求解如何分书,使得所有人满意。
思路:从小到大开始递归枚举每个人喜欢的书,找到第一组答案就可以结束。
代码实现:
int N;
boolean like[][] = new boolean [N][N];//行代表第几个人;列代表第几本书
int list[] = new int[N];//最终选择的书号
boolean vias[] = new boolean [N];//是否选完
public boolean dep(int m){
if(m == N)
return true;//递归终止的条件:所有人选完结束
for(int i=0;i<N;i++){
if(!vias[i] && like[m][i]){
list[m] = i+1;
vias[m] = true;
if(dep(m+1))
return true;
vias[m] = false;
}
}
return false;
}
【其他递归经典问题】
- 递归n的阶乘
- Fibonacci斐波那切数列
- 全排列问题
- 和树的遍历结合的相关问题(常考)