1 什么是递归调用
1.1 概念
递归调用是一种特殊的嵌套调用,是某个函数调用自己或者是调用其他函数后再次调用自己的,只要函数之间互相调用能产生循环的则一定是递归调用,递归调用一种解决方案,一种是逻辑思想,将一个大工作分为逐渐减小的小工作,比如说一个和尚要搬50块石头,他想,只要先搬走49块,那剩下的一块就能搬完了,然后考虑那49块,只要先搬走48块,那剩下的一块就能搬完了,递归是一种思想,只不过在程序中,就是依靠函数嵌套这个特性来实现了。
1.2 定义
递归调用就是在当前的函数中调用当前的函数并传给相应的参数,这是一个动作,这一动作是层层进行的,直到满足一般情况的的时候,才停止递归调用,开始从最后一个递归调用返回。
2、递归详情
调用前:
- 一个函数的运行期间调用另一个函数,在运行被调用函数之前,系统需要完成3件事
- 将所有的实参、返回地址等信息传递给被调用函数保存;
- 为被调用函数的成员变量分配存储区;
- 将控制转移到被调用的函数入口
调用中:
- 而从被调用函数返回调用函数之前,系统也应该完成3件工
- 保存被调函数的计算结果
- 释放被调函数的数据区
- 依照被掉函数保存的返回地址将控制转移到调用函数。当有多个函数构成嵌套调用时,按照后调用先返回的原则
递归函数特点:
- 所有递归函数的结构都是类似的。
- 函数要直接或间接调用自身。
- 要有递归终止条件检查,即递归终止条件满足后则不会再调用自身函数,一定要避免造成死循环
- 如果不满足递归终止的条件,则调用涉及递归调用的表达式。在调用函数自身时,有关终止条件的参数要发生变化,而却需要向递归中的方法变化
总结:
- 函数的调用原则和数据结构栈的实现式一致的。也说明函数调用时通过栈实现的。
3、递归的简单示例
- 案例一 :用递归方式实现99乘法表
public class test1112 {
@Test
public void test1() throws IOException {
cont(9);
}
public void cont(int num){
if (num==1){
System.out.println("1x1=1");
}else {
cont(num-1);
for (int i=1;i<=num;i++){
System.out.print(num+"x"+i+"="+i*num+" ");
}
System.out.println("");
}
}
}
//输出结果
1x1=1
2x1=2 2x2=4
3x1=3 3x2=6 3x3=9
4x1=4 4x2=8 4x3=12 4x4=16
5x1=5 5x2=10 5x3=15 5x4=20 5x5=25
6x1=6 6x2=12 6x3=18 6x4=24 6x5=30 6x6=36
7x1=7 7x2=14 7x3=21 7x4=28 7x5=35 7x6=42 7x7=49
8x1=8 8x2=16 8x3=24 8x4=32 8x5=40 8x6=48 8x7=56 8x8=64
9x1=9 9x2=18 9x3=27 9x4=36 9x5=45 9x6=54 9x7=63 9x8=72 9x9=81
思考: 为什么输出结果时,算式的结构顺序是这样的
- 这是因为函数的实现是通过栈实现的,符合栈顺序,虽未的栈顺序就像枪的弹夹一样
- 也可以参考这个图
- 案例二 :使用Java代码求5的阶乘
public class test1112 {
@Test
public void test1() throws IOException {
System.out.println(fn(5));
}
public long fn(int num){
if (num==1){
return 1;//设置了临界条件,当num-1=1的时候就结束递归
}else {
return num*fn(num-1);
}
}
}
- 案例三:使用Java代码求数列1,1,2,3,5,8…的第40位数
public class test1112 {
@Test
public void test1() throws IOException {
System.out.println(fn(40));
}
public long fn(int num){
if(1==num||2==num){
return 1;
}else {
return fn(num-1)+fn(num-2);
}
}
}