LeetCode之路:404. Sum of Left Leaves

来源:互联网 发布:mac flamingo 编辑:程序博客网 时间:2024/05/29 18:03

一、引言

这是一道乍看比较简单,但是稍不注意做起来就会比较痛苦的题目 T_T ~~~

这道题我做了很久,一开始审错了题目,提交了之后看到了 Error Case 之后发现自己理解错了,再之后做出了第一个版本的代码,但是对于代码的简洁度不够满意,然后又拼命为了减少代码行数思考更加简洁的办法,于是又写出了好几个版本的代码~~~

废话不多说,让我们先看题吧:

Find the sum of all left leaves in a given binary tree.

Example:
question

题目信息很简练,还是翻译一下:

请计算给定二叉树中所有的独立的左叶子节点的和。
举例:
这里有两个独立的左叶子节点,分别是 9 和 15,于是返回 24 即可。

这里非常简单,需要理解的就是左叶子节点的含义:

  1. 此节点一定拥有父节点
  2. 此节点一定没有子节点
  3. 此节点一定是父节点的左子节点

也就是说,满足了上述三个条件的节点,才是我们寻找的节点。

那么,这道题该如何做呢?

二、抓住重点:一定是父节点的左子节点

这道题乍一看好像非常简单的样子,仿佛我随便一个递归遍历就可以出来了 : )

于是我就写出了第一个错误的答案~~~

其实在引言里已经分析的非常清楚了,这里需要遍历整个二叉树寻找满足这些条件的节点:

没有子节点的、并且是其父节点的左子节点的节点

test binary tree

让我们一个一个条件的看:

  1. 该节点一定要有父节点:这个简单,我们只需要判断 root 是否为空即可(因为整个二叉树只有 root 节点没有父节点)

  2. 该节点没有子节点:这个其实也简单,我们需要拿到该节点,查看该节点的 left 和 right 指针是否为空

  3. 该节点一定是父节点的左子节点:这一条件或许比较难,但是仔细想想其实也比较好实现;我们遍历整个二叉树,当拿到父节点时,我们对于左子节点进行左叶子节点的寻找工作,而对于右子节点,我们只需要递归遍历即可,不需要进行左叶子节点的寻找工作

根据上述的逻辑,我写出了第一份通过了 Test Case 的代码:

// my solution 1 , runtime = 6 msclass Solution1 {public:    int sumOfLeftLeaves(TreeNode* root) {        if (root == nullptr) return 0;        if (root->left == nullptr && root->right == nullptr) return 0;        if (root->left) {            if (root->left->left == nullptr && root->left->right == nullptr) count += root->left->val;            else sumOfLeftLeaves(root->left);        }        if (root->right) sumOfLeftLeaves(root->right);        return count;    }private:    int count = 0;};

代码简单解释下:

  1. 首先,我对于 root 是否为空进行了判断,保证了“一定有父节点”这一条件

  2. 其次,我分成两个方向,其中右子节点的方向我们只需要递归遍历即可;左子节点有所不同,我们需要判断左子节点是否拥有子节点,如果没有的话,就意味着我们的左子节点为左叶子节点,那么我们就可以拿到这个节点的值进行计算

  3. 最后,我们按照第 2 步的逻辑遍历整个二叉树,最后得到所有左子节点的值的和返回即可

逻辑非常简单,但是代码写的我不满意,总想能不能再简化些。

于是我写了个只有 4 行的方法(使用了条件语句进行缩减):

// my solution 2 concise , runtime = 6 msclass Solution2 {public:    int sumOfLeftLeaves(TreeNode* root) {        if (!root || (!root->left && !root->right)) return 0;        if (root->left) !root->left->left && !root->left->right ? count += root->left->val : sumOfLeftLeaves(root->left);        if (root->right) sumOfLeftLeaves(root->right);        return count;    }private:    int count = 0;};

可能觉得这里的 root 和 root->left 和 root->right 没必要单起一行进行判断,又缩减了下逻辑:

// my solution 3 concise , runtime = 3 msclass Solution4 {public:    int sumOfLeftLeaves(TreeNode* root) {        if (root && root->right) sumOfLeftLeaves(root->right);        if (root && root->left) !root->left->left && !root->left->right ? count += root->left->val : sumOfLeftLeaves(root->left);        return count;    }private:    int count = 0;};

但是,这其实已经违背了代码的可读性这个要求,不过说实话,当代码写成了条件语句的时候,我自己来说,能够更清晰地看到我进行了几次判断,尽管逻辑都是一样的。

不过说实话,条件语句如此的疯狂的嵌套缩减代码是得不偿失的,我也就是玩玩而已 ^_^ ~~~

三、不使用私有变量:利用函数的返回值计算 sum 值

可能你也注意到了,我前三个方法都是定义了一个类私有变量 count 来计算和值。

可不可以不使用这个私有变量呢?

答案是可以的。

直接上代码吧:

// perfect solution , runtime = 6 msclass Solution4 {public:    int sumOfLeftLeaves(TreeNode* root) {        if (root == nullptr) return 0;        int count = 0;        if (root->left != nullptr) {            if (root->left->left == nullptr && root->left->right == nullptr) count += root->left->val;            else count += sumOfLeftLeaves(root->left);        }        count += sumOfLeftLeaves(root->right);        return count;    }};

这段代码的思路跟前三个方法的差不多,只是内部定义了一个 count 值进行计算和返回而已。

我们要记住这么一句话:

递归与循环不同,我们在思考递归的时候,不能理解为循环,递归是一种顺序执行的方法,也就是说你写的这个函数,当执行到了哪里,那么这行代码之前的所有逻辑都已经处理完成了

也就是说,看到这里的代码,当我们的代码执行到了 :

if (root->left != nullptr) {            if (root->left->left == nullptr && root->left->right == nullptr) count += root->left->val;            else count += sumOfLeftLeaves(root->left);}

我们就应该理解为,当前程序已经将当前的 root 节点的左子树 root->left 上的所有的左叶子节点的值计算完毕了。

当我们的代码执行到了 :

count += sumOfLeftLeaves(root->right);

的时候,我们的程序就已经将当前节点 root 的右子树 root->right 上的所有的左叶子节点的值计算完毕。

因此最后的:

return count;

就是我们所需要的所有的左叶子节点的和。

那么我们的探索也就到此为止了。

那么这道题只有这么一个思路吗?

在我查看了多个高票答案后,我很遗憾的发现可能还真是这样。很多方法都是写着不同的标题,但是其内涵都是一样的。

哈哈,不管怎么样,我们也算是通过自己的努力完成了这道题了吗不是?

四、总结

这道题我做了比较久,可能太想想出一个更简单的方法,可能是因为本身就只有这么一个方法了吧,所以更简单的方法可能很难找到。

不过递归仍然是一个非常有用的工具,尤其是在对待二叉树的问题上。

端午节就要到了,想想假期要干点什么呢?做做题,写写代码,写写博客,看看书,玩玩游戏什么的。

人生啊,就是这么美好 ^_^~~~