消除递归
一个算法作为一个递归的方法通常的概念理解是很容易的,但是递归的使用在方法的调用和返回都会有额外的开销,通常情况下,用递归能实现的,用循环都可以实现,而且循环的效率更高,所以实际运用中,把递归算法转换为非递归算法是很有用的,这种转换通常会运用到栈,
递归和栈
递归和栈有着紧密的联系,而且大多数编译器都是用栈来实现递归的,当调用一个方式时,编译器会把这个方法的所有参数和返回地址都压入栈中,然后把控制转移给这个方法,当这个方法返回时,这些值退栈,参数消失,并且把控制权重新返回地址处。
- 当一个方法被调用时,他的参数和返回地址被压入一个栈中;
- 这个方法可以通过获取栈顶元素来访问它的参数;
- 当这个方法要返回时,它可以查看栈以获得返回地址,然后这个地址以及方法的所有参数退栈并且销毁。
有趣些兔子问题
题目是 :有一对兔子,小兔子长到第三个月后,每个月会生一堆兔子,加入兔子不死的话,问每个月兔子的总数是多少。
首先我们要明白题目的意思是指每个月的兔子的总数,假设将兔子氛围小中大三种,兔子出生后每三个月生一对兔子
那么我们假定第一个兔子为小兔子,第二个月的兔子为中兔子,第三个月为大兔子,那个第一个月分别是1,0,0,第二个月的是0,1,1
第三个月的事1,0,1第四个月是1,1,1第五个月的是2,1,2第六个月为3,2,3第七个月为5,2,5.。。。
兔子的总数分别是1,1,2,3,5,8,13,,,
于是得出了一个规律,从第三个月开始,侯曼的兔子都是前面两个的总数之和,这就是斐波那契数列。
递归实现
@Test
public void Digui() {
int i = 1;
for(i=1;i<=12;i++){
System.out.println("兔子第"+i+"个月的总数为:"+f(i));
}
}
public static int f(int x) {
if (x == 1 || x == 2) {
return 1;
} else {
return f(x - 1) + f(x - 2);//当前月的兔子的个数等于前两个月兔子的相加
}
}
输出
非递归方法实现
@Test
public void getRubbitNum() {
int sun;//兔子每个月的总数
int new_rubbit = 1;//兔子第一个月的数(循环中代表的是上个月的数据)
int old_rubblt = 1;//兔子第二个月的数(循环中代表的是上上个月的数据)
for (int i = 3; i <= 12; i++) {
// 上面我说过第三个月开始每次兔子的数量等于上两个月之和
sun = old_rubblt + new_rubbit;//因为是第三个月开始的 所以第三个月的开始兔子为2只
// 这里表示的是当前月起算上上个月的赋值给上个月的,当下一次循环开始就会成为上上个月
old_rubblt = new_rubbit;
new_rubbit = sun;//将本月的兔子总数赋值 当下一次循环的时候将会变成上个月
System.out.println("第"+i+"个月的兔子总数为:"+sun);
}
输出
递归的小结
- 一个递归的方法每次用不同的参数值反复调用自身,
- 某个参数值时递归的方法返回,而不再调用自身,这种成为基值情况,
- 当递归方法返回时,递归过程通过质检完成各层方法实例的未执行部分,而从最内返回到最外层的原始调用处。
- 二分查找可以通过检查查找关键字在有系列的那一半,然后在这一办做相同的事情,这些都可以用递归实现
- 汉诺塔的问题半寒了三个塔座和任意数量的盘子,
- 汉诺塔难题可以用递归来解决,把出了最低端盘子外的所有盘子形成的字数移到一个中介塔,然后把最低端的盘子移到目标塔,就这样循环直到完成
- 归并两个有序的数组意思是创建第三个数组,这个数组按照顺序存储从这个两个有序中取到的所有数据项
- 归并排序需要一个大小等于原来数组的工作空间
- 对于汉诺塔和归并排序问题,他们的递归的方法包括两次递归调用
- 任何可以用递归完成的操作都可以用一个栈来实现
- 递归的方法可能效率低,如果是这样的话,有时可以用一个简单循环或者是一个基于栈的方法来代替