递归正如‘盗梦空间’中的场景:

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来缓存结果,你可以选择一小步或者一大步来走,使用递归可以非常方便地遍历所有的可能性。