万法之王,递归

858 阅读5分钟

什么是递归?

       递归介绍的文章已经非常多了,但是我发现它们都有一个共同的特点,都介绍得比较隐晦。很难抓住其本质,抓住重点;故写下此文给自己刷过的算法做一个小总结;

       所有的算法不过是 for / while / do while / if else / 递归 这些基础算法,再加上编程思维(头脑风暴)组成;其中最难的就是递归,我在leetCode专题上刷递归的时候,曾经也是一脸的茫然,发现它好难啊,真的好难;但如果你一旦掌握了它的本质和核心,它并非如此的难。在看本文的时候,希望小伙伴们对栈(LIFO)和树的数据结构有最基本的认识,如果不认识请自行查阅资料;

       相信很多小伙伴跟我当初一样,你说的我都知道,但是总是一看就会,一写就费。扎心了,铁汁。这个是没有办法的,需要多下功夫刻意练习。我强烈推荐把leetCode 树专题先刷起,因为树这个数据结构,它的本质上就是一个递归,用递归解决树的问题,是最合适的。

       递归本质上就是把大问题划分成N个小问题解决,然后通过N个小问题进行组合/计算得到最终我们想求的大问题。在进行递归的刻意练习时候,我们先来记住一个万能模板;

```js
   function recursion(...args){
      //1.base case 终止条件,也常叫出口;
      //2.process 本次递归你要解决的子问题,可能需要计算,组合什么的;
      //3.call self level 自己调用自己,进入下一层递归;
      //4.rest state 在第三步的时候可能需要改变一些递归公用的状态,将它们恢复;
   }
```

tips:初学递归写递归的时候我们都会经常犯错,就是人肉递归,一开始如果真的不是很熟悉可以这样,但是后面写熟悉了强烈建议不要人肉递归。人肉递归会让你非常的混乱,再说人脑能进行几层递归呢?QAQ

递归的本质

上面我说到了,递归的本质之一就是一棵树,用我们最熟悉不过的斐波那契数列来举例;

我相信很多小伙伴闭着眼睛都能知道斐波那契数列怎么求,不就是 f(n) = f(n - 1) + f(n - 2)

如果用递归写,就直接是照抄公式,写好终止条件就行了,没错,确实是这样;但是为什么递归能如此的简洁就把问题解决了呢?递归的本质又是什么呢?我相信是有很大部分同学是没有完全搞清楚的,没有搞清楚这一点,在写比较难的递归时候,就会陷入不会写的局面;


利用网上的一张图,我们人肉递归分析一下递归的状态树,要计算第四项斐波那契数列的值,就要知道第三项第二项,以此递归;递归到我们可以明确的知道最前面的那两项。就可以得到最终的解了。

在这里,我们写递归之前我们要明确两个点,也是写递归必须要分析清楚的两个点。

  1. 解决的问题能够分解成N个子问题;能够利用子问题的解来组合计算得到最终的解。
  2. 递归非常的适合人脑的思考方式,只要把上面模板的四个问题想清楚,写递归真的不难。

我们来解一下这个递归

```js
   function recursion(n){
      //1.base case 终止条件是什么?因为 f(n) = f(n - 1) + f(n - 2);
      //  所以终止条件必须是两项,第0项是0,第一项是1
      if(n == 0) return 0;
      if(n == 1) return 1;
      //2.process 本次递归你要解决的子问题,我们要计算第N项的值,那么就要知道N-1和N-2项;
      //3.call self 进入下一层递归;千万不要脑子人肉进递归里面去想,recursion(n - 1) 肯定能得到值      
      let cur = recursion(n - 1)
      let pre = recursion(n - 2)
      //4.rest state 在第三步的时候可能需要改变一些递归公用的状态,将它们恢复;
      //在这里不需要重置任何公用的值
      //最后我们返回最终的解,就是前两项的值之和,cur + pre,你不要去人肉递归想,进入函数去计算!
      return pre + cur
   }```

接下来我以leetcode真题带大家感受一下这个模板,这里偷个懒直接vs code + 插件AC一些经典的递归题了。

1.二叉树最大深度(easy)

直接套模板,然后想清楚递归的四个问题;这里我直接给出 原题 链接,根据题目思考问题。


2.平衡二叉树(easy)


3.路径总和 II(Medium)


让你们感受一下模板的魅力QAQ


总结

       递归的技巧远远不止这些,还有一些优化的技巧就不展开说了。递归关键的是多练,根据模板把四个问题想清楚就行了,一开始你可以人肉递归,后面熟悉了千万不要再人肉递归了。

       可能有的小伙伴说递归的性能不好呀,什么的;不存在的,如果你写的是傻递归,性能确实存在很大的问题,如果你利用一些手段优化,想清楚递归状态树,提前剪枝,或者尾递归优化,性能也是一样的;重点是,递归真的很适合人脑的思考方式进行编程,很简洁。但是注意千万不要人肉递归;容易混乱。