递归中全局变量的覆盖

1. 问题

404. 左叶子之和 - 力扣(LeetCode) (leetcode-cn.com)

在计算上面题目时,需要使用到递归来计算左叶子之和,代码如下

class Solution {
    int res = 0;
    public int sumOfLeftLeaves(TreeNode root) {
        if(root==null)
            return 0;
        dfs(root);
        return res;
    }
    private int dfs(TreeNode root) {
        if(root==null)
            return 0;
        if(root.left==null&&root.right==null)
            return root.val;
        //图中字节码文件部分
        /------------------/
        int left = dfs(root.left);
        res+=left;
        /------------------/
        dfs(root.right);
        return 0;
    }
}

上面代码可以得到正确答案

如果将dfs中的left变量去掉,直接+=,代码如下

private int dfs(TreeNode root) {
    if(root==null)
        return 0;
    if(root.left==null&&root.right==null)
        return root.val;
    //图中字节码文件部分
    /------------------/
    res+=dfs(root.left);
    /------------------/
    dfs(root.right);
    return 0;
}

那么,其不能得到正确答案!

2. 分析

下面来比较两段代码的字节码文件

  • 正确版本

    image-20210425205823520

    下面按顺序分析

    • aload_0

      压入this

    • aload_1

      压入root

    • getfield

      弹出root,取出root.left并压入栈

    • invokevirtual

      弹出root.left和this,调用this的dfs方法,方法参数为root.left,返回值压入栈

    • istore_2

      将方法返回值从栈顶弹出,存入局部变量left

    • aload_0

      压入this

    • dup

      复制this引用,并压入

    • getfield

      弹出this引用复制,取得res变量,并压入res

    • iload_2

      压入left变量

    • iadd

      弹出left变量和res变量,求和,将和压入

    • putfield

      弹出this引用及上一步的和,将和赋值给this中的res

    从上可见,在dfs(root.left)方法调用前后,分别压入了两次this来获取最新的res的值

  • 错误版本

    image-20210425205530463

    下面按顺序分析

    • aload_0

      压入this

    • dup

      复制this引用,并压入

    • getfield

      弹出this引用复制,取得res变量,并压入res

    • aload_0

      压入this

    • aload_1

      压入root

    • getfield

      弹出root,取出root.left并压入栈

    • invokevirtual

      弹出root.left和this,调用this的dfs方法,方法参数为root.left,返回值压入栈

    • iadd

      弹出返回值以及res,求和,将和压入栈

    • putfield

      弹出和以及this,将和复制给res

    这里注意到一个问题,即只在dfs(root.left)前发生了this的压入以及res值得获取,这样每一次递归获取到的都是进入当前递归时旧对象的值,也即更深层的递归放入this的res,会被较浅层的res覆盖,这样返回值就会出现错误

此题的更好的解法如下

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root==null)
            return 0;
        return dfs(root.left,true)+dfs(root.right,false);
    }
    private int dfs(TreeNode root,boolean isLeft) {
        if(root==null)
            return 0;
        if(root.left==null&&root.right==null){
            return isLeft?root.val:0;
        }
        return dfs(root.left,true)+dfs(root.right,false);
    }
}

3. 总结

从上可以看出,这个问题出现的根本原因在于没有在递归调用结束后重新获取对象的引用,从而导致深层递归的值不能在浅层递归中取得

posted @ 2021-04-25 21:19  Linus1  阅读(186)  评论(0编辑  收藏  举报