AP计算机教程10-2:跟踪递归方法
在Java中,调用堆栈(call stack)追踪在main
method执行后你所调用的所有method。栈(stack)是一种组织数据的方式,仅在栈顶添加或删除项目。日常生活中一沓杯子能很好的展示计算机科学中栈的概念。你可以从顶上取一个杯子,也可以套上更多的杯子。
当你执行method a,而它又调用了method b的时候,调用的状态会被添加到堆栈上,这样在调用完成以后能够返回到先前的位置继续执行。当method b执行完并返回后,调用的状态会从堆栈上弹出,开始恢复method a的执行环境并继续执行后续的语句。思考以下这个class定义。
public class StackTest { public static void test1() { System.out.println("In test1"); test2(); System.out.println("In test1 after test2"); } public static void test2() { System.out.println("In test2"); int y = 0; int x = 3 / y; } public static void main(String[] args) { System.out.println("In main"); test1(); } }
以上的代码会在执行时产生除零错。main
method在第16行调用test1
method,而test1
method又在第4行调用test2
method,其在第11行产生错误。这些都可以在错误信息中看到。
当某个method调用自身时,新的调用会不断加到堆栈上。当前method的执行会暂停,以等待递归调用完成并返回。让我们来以下面的阶乘method为例说明。
public static int factorial(int n) { if (n == 0) return 1; else return n * factorial(n-1); }
当调用factorial(0)
时会发生什么?从代码上看会直接在第4行返回1
。对于factorial(0)
,则会返回1 * factorial(0)
。我们已经知道factorial(0)
是1
,但是对于计算机而言还需要重新计算。返回factorial(0)
后,我们有1 * 1
,最终结果为1
。
因此我们可以跟踪递归的执行过程,自底而上的记下调用堆栈(即先从堆栈的底部开始写)。如果在考试中遇到计算factorial(5)
,草稿纸上的整个追踪过程会是这样。
factorial(5) returns 5 * factorial(4) factorial(4) returns 4 * factorial(3) factorial(3) returns 3 * factorial(2) factorial(2) returns 2 * factorial(1) factorial(1) returns 1 * factorial(0) factorial(0) returns 1
当factorial(0)
执行完成之后,可以把每一步的结果代回到上一步中,模拟堆栈的弹出。这样我们又可以一步一步回到栈底(草稿纸上的顺序是从下往上)。
factorial(5) returns 5 * factorial(4) = 5 * 24 = 120 factorial(4) returns 4 * factorial(3) = 4 * 6 = 24 factorial(3) returns 3 * factorial(2) = 3 * 2 = 6 factorial(2) returns 2 * factorial(1) = 2 * 1 = 2 factorial(1) returns 1 * factorial(0) = 1 * 1 = 1 factorial(0) returns 1
因此factorial(5)
的结果是120
。可以使用Jeloit这一工具来查看调用堆栈。
0:00
Given the method defined below what does the following return: factorial(6)
?
public static int factorial(int n) { if (n == 0) return 1; else return n * factorial(n-1); }
factorial(5)
再乘以6
。Given the method defined below what does the following return: mystery(5)
?
public static int mystery(int n) { if (n == 0) return 1; else return 2 * mystery (n - 1); }
2
的五次方。Given the method defined below what does the following print: mystery(4,3)
?
public static int mystery(int n, int a) { if (n == 1) return a; return a * mystery(n-1,a); }
3
的四次方。我们可以再看看下面bunnyEars
method的执行。
public static int bunnyEars(int bunnies) { if (bunnies == 0) return 0; else if (bunnies == 1) return 2; else return 2 + bunnyEars(bunnies - 1); }
调用bunnyEars(0)
时会发生什么?从代码上看会直接在第3行返回0
。bunnyEars(1)
呢?,会在第4行返回2
。对于bunnyEars(5)
,我们有:
bunnyEars(5) returns 2 + bunnyEars(4) bunnyEars(4) returns 2 + bunnyEars(3) bunnyEars(3) returns 2 + bunnyEars(2) bunnyEars(2) returns 2 + bunnyEars(1) bunnyEars(1) returns 2
重复以上的步骤,我们可以得到。
bunnyEars(5) returns 2 + bunnyEars(4) = 2 + 8 = 10 bunnyEars(4) returns 2 + bunnyEars(3) = 2 + 6 = 8 bunnyEars(3) returns 2 + bunnyEars(2) = 2 + 4 = 6 bunnyEars(2) returns 2 + bunnyEars(1) = 2 + 2 = 4 bunnyEars(1) returns 2
Given the method defined below what does the following print: mystery(1234)
?
public static void mystery (int x) { System.out.print(x % 10); if ((x / 10) != 0) { mystery(x / 10); } System.out.print(x % 10); }
print
,故是先倒序再顺序。Given the method defined below what does the following return: mystery("xyzxyxy")
?
public static int mystery(String str) { if (str.length() == 1) return 0; else { if (str.substring(0,1).equals("y")) return 1 + mystery(str.substring(1)); else return mystery(str.substring(1)); } }
y
出现的次数,但问题在于执行到最后一个字符时就直接返回0
了,因此对于所给的字符串会少算一个。
0 条评论