Given a binary tree, count the number of uni-value subtrees.

A Uni-value subtree means all nodes of the subtree have the same value.

Example :

Input:  root = [5,1,5,5,5,null,5]

              5
/ \
1 5
/ \ \
5 5 5 Output: 4

这道题让我们求相同值子树的个数,就是所有节点值都相同的子树的个数,之前有道求最大 BST 子树的题 Largest BST Subtree,感觉挺像的,都是关于子树的问题,解题思路也可以参考一下,这里可以用递归来做,第一种解法的思路是先序遍历树的所有的节点,然后对每一个节点调用判断以当前节点为根的字数的所有节点是否相同,判断方法可以参考之前那题 Same Tree,用的是分治法的思想,分别对左右字数分别调用递归,参见代码如下:

解法一:

class Solution {
public:
int res = ;
int countUnivalSubtrees(TreeNode* root) {
if (!root) return res;
if (isUnival(root, root->val)) ++res;
countUnivalSubtrees(root->left);
countUnivalSubtrees(root->right);
return res;
}
bool isUnival(TreeNode *root, int val) {
if (!root) return true;
return root->val == val && isUnival(root->left, val) && isUnival(root->right, val);
}
};

但是上面的那种解法不是很高效,含有大量的重复 check,我们想想能不能一次遍历就都搞定,这样想,符合条件的相同值的字数肯定是有叶节点的,而且叶节点也都相同(注意单独的一个叶节点也被看做是一个相同值子树),那么可以从下往上 check,采用后序遍历的顺序,左右根,这里还是递归调用函数,对于当前遍历到的节点,如果对其左右子节点分别递归调用函数,返回均为 true 的话,那么说明当前节点的值和左右子树的值都相同,那么又多了一棵树,所以结果自增1,然后返回当前节点值和给定值(其父节点值)是否相同,从而回归上一层递归调用。这里特别说明一下在子函数中要使用的那个单竖杠或,为什么不用双竖杠的或,因为单竖杠的或是位或,就是说左右两部分都需要被计算,然后再或,C++ 这里将 true 当作1,false 当作0,然后进行 Bit OR 运算。不能使用双竖杠或的原因是,如果是双竖杠或,一旦左半边为 true 了,整个就直接是 true 了,右半边就不会再计算了,这样的话,一旦右子树中有值相同的子树也不会被计算到结果 res 中了,参见代码如下:

解法二:

class Solution {
public:
int countUnivalSubtrees(TreeNode* root) {
int res = ;
isUnival(root, -, res);
return res;
}
bool isUnival(TreeNode* root, int val, int& res) {
if (!root) return true;
if (!isUnival(root->left, root->val, res) | !isUnival(root->right, root->val, res)) {
return false;
}
++res;
return root->val == val;
}
};

我们还可以变一种写法,让递归函数直接返回以当前节点为根的相同值子树的个数,然后参数里维护一个引用类型的布尔变量,表示以当前节点为根的子树是否为相同值子树,首先对当前节点的左右子树分别调用递归函数,然后把结果加起来,现在要来看当前节点是不是和其左右子树节点值相同,当前首先要确认左右子节点的布尔型变量均为 true,这样保证左右子节点分别都是相同值子树的根,然后看如果左子节点存在,那么左子节点值需要和当前节点值相同,如果右子节点存在,那么右子节点值要和当前节点值相同,若上述条件均满足的话,说明当前节点也是相同值子树的根节点,返回值再加1,参见代码如下:

解法三:

class Solution {
public:
int countUnivalSubtrees(TreeNode* root) {
bool b = true;
return isUnival(root, b);
}
int isUnival(TreeNode *root, bool &b) {
if (!root) return ;
bool l = true, r = true;
int res = isUnival(root->left, l) + isUnival(root->right, r);
b = l && r && (root->left ? root->val == root->left->val : true) && (root->right ? root->val == root->right->val : true);
return res + b;
}
};

上面三种都是令人看得头晕的递归写法,那么我们也来看一种迭代的写法,迭代写法是在后序遍历 Binary Tree Postorder Traversal 的基础上修改而来,需要用 HashSet 来保存所有相同值子树的根节点,对于遍历到的节点,如果其左右子节点均不存在,那么此节点为叶节点,符合题意,加入结果 HashSet 中,如果左子节点不存在,那么右子节点必须已经在结果 HashSet 中,而且当前节点值需要和右子节点值相同才能将当前节点加入结果 HashSet 中,同样的,如果右子节点不存在,那么左子节点必须已经存在 HashSet 中,而且当前节点值要和左子节点值相同才能将当前节点加入结果 HashSet  中。最后,如果左右子节点均存在,那么必须都已经在 HashSet 中,并且左右子节点值都要和根节点值相同才能将当前节点加入结果 HashSet 中,其余部分跟后序遍历的迭代写法一样,参见代码如下:

解法四:

class Solution {
public:
int countUnivalSubtrees(TreeNode* root) {
if (!root) return ;
unordered_set<TreeNode*> res;
stack<TreeNode*> st{{root}};
TreeNode *head = root;
while (!st.empty()) {
TreeNode *t = st.top();
if ((!t->left && !t->right) || t->left == head || t->right == head) {
if (!t->left && !t->right) {
res.insert(t);
} else if (!t->left && res.find(t->right) != res.end() && t->right->val == t->val) {
res.insert(t);
} else if (!t->right && res.find(t->left) != res.end() && t->left->val == t->val) {
res.insert(t);
} else if (t->left && t->right && res.find(t->left) != res.end() && res.find(t->right) != res.end() && t->left->val == t->val && t->right->val == t->val) {
res.insert(t);
}
st.pop();
head = t;
} else {
if (t->right) st.push(t->right);
if (t->left) st.push(t->left);
}
}
return res.size();
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/250

类似题目:

Subtree of Another Tree

Longest Univalue Path

Largest BST Subtree

Binary Tree Postorder Traversal

Same Tree

参考资料:

https://leetcode.com/problems/count-univalue-subtrees/

https://leetcode.com/problems/count-univalue-subtrees/discuss/67602/Java-11-lines-added

https://leetcode.com/problems/count-univalue-subtrees/discuss/67644/AC-clean-Java-solution

https://leetcode.com/problems/count-univalue-subtrees/discuss/67573/My-Concise-JAVA-Solution

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Count Univalue Subtrees 计数相同值子树的个数的更多相关文章

  1. [LeetCode] 250. Count Univalue Subtrees 计算唯一值子树的个数

    Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of ...

  2. [Swift]LeetCode250.计数相同值子树的个数 $ Count Univalue Subtrees

    Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of ...

  3. [leetcode]250. Count Univalue Subtrees统计节点值相同的子树

    Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of ...

  4. [Locked] Count Univalue Subtrees

    Count Univalue Subtrees Given a binary tree, count the number of uni-value subtrees. A Uni-value sub ...

  5. [LeetCode#250] Count Univalue Subtrees

    Problem: Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all ...

  6. 250. Count Univalue Subtrees

    题目: Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes ...

  7. [LC] 250. Count Univalue Subtrees

    Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of ...

  8. [LeetCode] Count Complete Tree Nodes 求完全二叉树的节点个数

    Given a complete binary tree, count the number of nodes. Definition of a complete binary tree from W ...

  9. [LeetCode] Count and Say 计数和读法

    The count-and-say sequence is the sequence of integers beginning as follows:1, 11, 21, 1211, 111221, ...

随机推荐

  1. OWIN与Katana详解

    前言 我胡汉三又回来了,!!!!, 最近忙成狗,实在没空写博文,实在对不起自己,博客园上逛了逛发现 我大微软还是很给力的 asp.net core 1.0 .net core 1.0 即将发布,虽然. ...

  2. ASP.NET Core 中文文档 第二章 指南(4.6)Controller 方法与视图

    原文:Controller methods and views 作者:Rick Anderson 翻译:谢炀(Kiler) 校对:孟帅洋(书缘) .张仁建(第二年.夏) .许登洋(Seay) .姚阿勇 ...

  3. 深入学习jQuery描述文本内容的3个方法

    × 目录 [1]html() [2]text() [3]val()[4]总结 前面的话 在javascript中,描述元素内容有5个属性,分别是innerHTML.outerHTML.innerTex ...

  4. 利用KD树进行异常检测

    软件安全课程的一次实验,整理之后发出来共享. 什么是KD树 要说KD树,我们得先说一下什么是KNN算法. KNN是k-NearestNeighbor的简称,原理很简单:当你有一堆已经标注好的数据时,你 ...

  5. Ionic2系列-将beta升级到RC1

    国庆节前Ionic2发布了RC0版本,已经接近正式版了,前不久Angular2和TypeScript2也已经发布了正式版.详情请参考官方博客: http://blog.ionic.io/announc ...

  6. C#基础知识八之访问修饰符

    1.  类的访问修饰符 修饰符 访问权限 无或者internal 只能在同一个程序集中访问类 public 同一个程序集或引用该程序集的外部都可访问类 abstract或internal abstra ...

  7. C#开发微信门户及应用(10)--在管理系统中同步微信用户分组信息

    在前面几篇文章中,逐步从原有微信的API封装的基础上过渡到微信应用平台管理系统里面,逐步介绍管理系统中的微信数据的界面设计,以及相关的处理操作过程的逻辑和代码,希望从更高一个层次,向大家介绍微信的应用 ...

  8. SpringAOP与Redis搭建缓存

    近期项目查询数据库太慢,持久层也没有开启二级缓存,现希望采用Redis作为缓存.为了不改写原来代码,在此采用AOP+Redis实现. 目前由于项目需要,只需要做查询部分: 数据查询时每次都需要从数据库 ...

  9. LAMP布署笔记

    源代码软件的优点:     获得最新版,能及时修复bug:     能自行修改和定制: 源代码打包形式:     .tar.gz和.tar.bz2格式居多: 完整性校验:     md5sum校验工具 ...

  10. python语言中的编码问题(续)

    上文提到了python开发中非常重要的两处设置. 一个是编解码器的默认设置defaultencoding >>> import sys >>> sys.getdef ...