递归
递归正如‘盗梦空间’中的场景:
PS: 使用《windows用户, 截屏新手段》可以一句for循环生成类似这种效果的图。
简言之就是不断调用自己,
经常我们会拿fibonacci数来讲解递归,正如在Rabbits and Recurrence
Relations一题中所实现的,我使用了静态变量来缓存,以加快运行速度。R版本的实现可以参考tricky things in R一文,相对应于C的静态变量,在R中使用了局部的全局变量,这需要用<<-
来赋值。
在开发ggtree时,unrooted layout最早的版本就是使用递归实现,但BiocCheck
会把<<-
报出来,而Bioconductor的人不喜欢看到这个赋值符,于是我改成了现在的版本,使用for loop。递归和循环一样,都只是程序的一种控制机制,某些递归实现代码非常短的同时,可读性非常强。
Example 1
比如我们要写writeSequence这样的函数:
Call Output
writeSequence(1); 1
writeSequence(2); 1 1
writeSequence(3); 2 1 2
writeSequence(4); 2 1 1 2
writeSequence(5); 3 2 1 2 3
writeSequence(6); 3 2 1 1 2 3
public void writeSequence(int n) {
if (n < 1)
throw new IllegalArgumentException();
if (n == 1) {
System.out.print(1);
return;
}
if (n == 2) {
System.out.print("1 1");
return;
}
int x = (int) Math.ceil(n/2.0);
System.out.print(x + " ");
writeSequence(n-2);
System.out.print(" " + x);
}
先定义跳出递归的base case,然后就是不断调用自己,把结果插在中间。
Example 2
List L Limit n maxSum(L, n) returns
[7, 30, 8, 22, 6, 1, 14] 19 16
[5, 30, 15, 13, 8] 42 41
[30, 15, 20] 40 35
[6, 2, 6, 9, 1] 30 24
[11, 5, 3, 7, 2] 14 14
[10, 20, 30] 7 0
[10, 20, 30] 20 20
[] 10 0
再比如这个例子,数列中各种组合的总数,最大但不超过limit的数。
public int maxSum(List list, int n) {
if (n <= 0 || list.size() == 0)
return 0;
int item = list.get(0);
list.remove(0);
int maximum;
if (n < item) {
maximum = maxSum(list, n);
} else {
int with = item + maxSum(list, n-item);
int without = maxSum(list, n);
maximum = Math.max(with, without);
}
list.add(0, item);
return(maximum);
}
如果元素不超过n,可加可不加,然后递归应用到去掉这个元素的数列中。在调用的结尾,我用了list.add()来把删掉的元素放回去,因为我们并不希望调用此函数之后,数列变为空。
Example 3
假如你在爬台阶,一小步是一个台阶,一大步是两个台阶,那么爬4个台阶有以下5种可能性:
[1, 1, 1, 1]
[1, 1, 2]
[1, 2, 1]
[2, 1, 1]
[2, 2]
实现一个函数waysToClimb(n),返回爬n个台阶的所有方式。
public void waysToClimb(int n) {
Stack steps = new Stack();
waysToClimb(n, steps);
}
public void waysToClimb(int n, Stack steps) {
if (n <= 0) {
System.out.println(steps);
return;
} else {
steps.push(1); // choose 1
waysToClimb(n-1, steps); // explore
steps.pop(); // un-choose
if (n > 1) {
steps.push(2);
waysToClimb(n-2, steps);
steps.pop();
}
}
}
这里使用了Stack来缓存结果,你可以选择一小步或者一大步来走,使用递归可以非常方便地遍历所有的可能性。