美文网首页
关于递归的一点理解

关于递归的一点理解

作者: YocnZhao | 来源:发表于2019-07-08 17:05 被阅读0次

什么是递归,一幅图表示:


递归是穷举的一种方式。本篇文章并不涉及递归定义的讲解,只是本人对递归的一点小理解。
结论:部分递归可以归纳成一个N叉树状的逻辑结构
比如这个题目:
x个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完

拿到这问题第一时间知道这肯定要用递归来解决,但是总觉着无处下手。
这个时候我们不妨找一个我们最熟悉的案例来过一下递归的结构,那我们掏出BinaryTree的DFS

     public void visitNode(TreeNode node, Bean bean) {
        if (node == null) {
            return;
        }
        bean.num++;
        visitNode(node.left, bean);
        visitNode(node.right, bean);
    }

发现其实思想是类似的,一个是递归左右子树,一个是递归三种情况。那是不是说这个题目也可以同样的用二叉树或者N叉树的结构表现出来呢?


那大致就长这个样子,我们要做的就是用DFS遍历这棵三叉树。

  1. 节点表示的是每天吃了一个两个还是三个苹果,
  2. 树的高度表示我们花了多少次循环完成找到的结果。
  3. 叶子节点表明递归条件终止

其实我们自己去想的时候也是这样子,列出所有的集合,然后找到符合条件的情况。只不过树状结构比较直观,列出了所有的情况。

代码如下所示:

public class XApple {
    public void test() {
        eatApple(3, 0, "");
    }

    private void eatApple(int x, int day, String result) {
        eatAppleEveryDay(x, 1, day, result);
        eatAppleEveryDay(x, 2, day, result);
        eatAppleEveryDay(x, 3, day, result);
    }

    /**
     * @param left     还剩苹果个数
     * @param everyDay 每天吃的个数
     * @param day      无意义,打印用,第几天
     * @param result   无意义,打印用,结果传递
     */
    private void eatAppleEveryDay(int left, int everyDay, int day, String result) {
        result = result + "第" + day + "天吃" + everyDay + "个|";
        left -= everyDay;

        if (left < 0) {
            return;
        }
        if (left == 0) {
            System.out.print(result + "\n");
            return;
        }
        eatApple(left, ++day, result);
    }
}

虽然有的递归可以抽象成N叉树的方式来加深理解,比如全排列,N皇后,汉诺塔等经典递归问题(这里又涉及到回溯)。
但是有一些需要用递归的就无法抽象成N叉树的样子,比如斐波那契数列,阶乘等就不好抽象,所以还是有局限性。

我们直观上看这几个方法是顺序执行的:

        eatAppleEveryDay(x, 1, day, result);
        eatAppleEveryDay(x, 2, day, result);
        eatAppleEveryDay(x, 3, day, result);

就有时候总会有一种错觉,就是这几个方法会按照123的顺序来顺序执行,吃一个总是在最前面,那岂不是每次都需要先吃一个苹果才会走后面的2跟3。
那我们再简化一下,4个苹果,每天吃一个或者两个,我们去掉打印需要,那代码如下所示。

    public void test() {
        eat(4, 0);
    }

    private void eat(int x, int day) {
        eatPerDay(x, 1, day);
        eatPerDay(x, 2, day);
    }

    private void eatPerDay(int left, int everyDay, int day) {
        left -= everyDay;
        if (left <= 0) {
            return;
        }
        eat(left, ++day);
    }

我们自己来想,总会有一个答案是第一天吃两个,第二天吃两个。也就是执行了两次eatPerDay(x, 2, day);, 嗯?这个2-2是怎么来的呢?
为什么没有执行吃一个的代码也就是eatPerDay(x, 1, day);?怎么可能不执行?
不执行第一句是怎么执行到第二句的?

最浅显容易理解的答案是1-1-1-1,也就是4天每天吃1个。这个打到条件return之后会执行1-1-1-2,当然这个是不满足条件的,那这个是怎么执行过去的呢?越来越迷糊,感觉对程序运行产生了怀疑。
再退一步,如果我们什么都不干预,只需要执行4层,是会产生2^4=16中答案的,我们从中挑出我们想要的摒弃掉不想要的,树状结构就是这么来的,树状结构只是结构,并不代表执行的次数只会执行一次。
如果我们把需要执行4层的所有的结果都不加控制的输出也就是从左往右深度遍历二叉树:
1-1-1-1
1-1-1-2
1-1-2-1
...
2-2-1-1
...
2-2-2-2
一共16条结果,这其中2-2-1-1其实是我们需要的结果,只不过我们只需要它的主干,后面的1-1不需要所以直接就不往后执行了,并不是2-2凭空开始执行的。

这里写一下加深理解,自己每次看到这个XApple也会懵一阵子,好记性不如烂笔头,写下来好了。

相关文章

  • 关于递归的一点理解

    什么是递归,一幅图表示: 拿到这问题第一时间知道这肯定要用递归来解决,但是总觉着无处下手。这个时候我们不妨找一个我...

  • 直观理解(尾)递归函数

    前言 我们都见识了不少关于递归与尾递归的各种长篇概论,本文将通过对下面几个问题的直观体验,来帮助加深对递归的理解。...

  • CSI讲义8:理解递归

    所有的计算都是递归;要理解递归首先要理解递归。 程序设计思想之一“递归”历来是同学们的理解难点。据说,**要理解递...

  • 数据结构之理解递归

    理解递归 要理解递归, 首先要理解递归 --佚名 递归是一种解决问题的方法, 他从解决问题的各个小部分开始, 知道...

  • 理解递归

    反转链表 还是老样子,我们先假设n-1个链表已经反转完成了: 那么此时我们应该怎么做呢?此时1这个元素的next指...

  • 理解递归

    从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和...

  • 理解递归

    递归的概述 摘取维基百科关于递归的描述:递归(英语:Recursion),又译为递回,在数学[https://zh...

  • 关于递归的一点想法

    1.优化 如下图所示,递归计算的时候,重复计算了两次 f(5),五次 f(4)。。。。这是非常恐怖的,n 越大,重...

  • 关于递归的一点感悟

    面试题 然后我们就可以写出我们的递归代码 自己的想法 我们首先根据题意,一步步得出符合题意的公式,最好写出关于n的...

  • 第十节-递归

    这节课的思路主要是如何理解递归 --> 递归的三个条件 --> 如何编写递归 --> 递归要注意的点 --> 怎么...

网友评论

      本文标题:关于递归的一点理解

      本文链接:https://www.haomeiwen.com/subject/cjqahctx.html