递归


去的过程叫“递”,回来的过程叫“归”
写递归代码的关键就是找到如何将大问题分解为小问题的规律,并且基于此写出递推公式,然后再推敲终止条件,最后将递推公式和终止条件翻译成代码
编写递归代码的关键是,只要遇到递归,我们就把它抽象成一个递推公式,不用想一层层的调用关系,不要试图用人脑去分解递归的每个步骤

递归与循环没有明显的边界,它是通过函数体来进行的循环

界定问题能否用递归解决

  • 一个问题的解可以分解为几个子问题的解
  • 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
  • 存在递归终止条件

编写递归代码的技巧

终止条件 递推公式

注意的点

  • 警惕堆栈溢出

递归会利用栈保存临时变量,如果递归过深,会造成栈溢出。解决方案是控制递归的深度

  • 警惕重复计算

递归要警惕重复计算,递归分解的子问题、子子问题可能存在相同的情况,如果都一一计算的话,就会发生重复计算。解决方案是使用散列表来保存结算结果,每次开始计算前检查散列表是否已经有结算结果

递归优化

笼统地讲,递归代码都能用迭代循环来替换

递归一般流程

1. terminator
2. process current logic
3. drill down
4. 恢复当前层

python 代码模版

def recursion(level, param1, param2,...):
  # recuision terminator 递归终结条件
  if level > MAX_LEVEL:
    process_result
    return

  # process logic in current level 处理当前层逻辑
  process(level, data...)

  # drill down 下探到下一层
  self.recursion(level+1, p1,...)

  # reverse the current level status if needed 清理当前层

Java 代码模版

public void recur(int level, int param) {
  // terminator
  if (level > MAX_LEVEL) {
    // process result
    return
  }

  // process current logic
  process(level, param)

  // drill down
  recur(level:level+1, newParam)

  // restore current status
}

思维要点

  • 不要人肉递归
  • 找到最近重复子问题
  • 数学归纳法思维

leetcode 题目

22.括号生成
70.爬楼梯

其它资源

如何优雅地计算斐波那契数列