文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:统计二叉树中好结点的数目
出处:1448. 统计二叉树中好结点的数目
难度
5 级
题目描述
要求
给定一个二叉树的根结点 root \texttt{root} root,如果从根结点到结点 X 的路径上没有任何结点的值大于 X 的值,则结点 X 是好结点。
返回二叉树中好结点的数目。
示例
示例 1:
输入:
root
=
[3,1,4,3,null,1,5]
\texttt{root = [3,1,4,3,null,1,5]}
root = [3,1,4,3,null,1,5]
输出:
4
\texttt{4}
4
解释:图中蓝色结点是好结点。
根结点
(3)
\texttt{(3)}
(3) 总是好结点。
结点
4
→
(3,4)
\texttt{4} \rightarrow \texttt{(3,4)}
4→(3,4) 是路径中的最大值。
结点
5
→
(3,4,5)
\texttt{5} \rightarrow \texttt{(3,4,5)}
5→(3,4,5) 是路径中的最大值。
结点
3
→
(3,1,3)
\texttt{3} \rightarrow \texttt{(3,1,3)}
3→(3,1,3) 是路径中的最大值。
示例 2:
输入:
root
=
[3,3,null,4,2]
\texttt{root = [3,3,null,4,2]}
root = [3,3,null,4,2]
输出:
3
\texttt{3}
3
解释:结点
2
→
(3,
3,
2)
\texttt{2} \rightarrow \texttt{(3, 3, 2)}
2→(3, 3, 2) 不是好结点,因为
3
\texttt{3}
3 比它大。
示例 3:
输入:
root
=
[1]
\texttt{root = [1]}
root = [1]
输出:
1
\texttt{1}
1
解释:根结点是好结点。
数据范围
- 树中结点数目在范围 [1, 10 5 ] \texttt{[1, 10}^\texttt{5}\texttt{]} [1, 105] 内
- -10 4 ≤ Node.val ≤ 10 4 \texttt{-10}^\texttt{4} \le \texttt{Node.val} \le \texttt{10}^\texttt{4} -104≤Node.val≤104
解法一
思路和算法
二叉树中的每个结点对应一条从根结点到该结点的路径。如果一个结点是好结点,则从根结点到该结点的路径上的每个结点值都不超过该结点值,即路径上的最大结点值不超过该结点值。
可以使用深度优先搜索判断二叉树中的每个结点是否是好结点。从根结点开始遍历全部结点,由于同一条路径上的结点的遍历顺序是从上到下,在访问到一个结点时,该结点的所有祖先结点都已经被访问过,因此可以得到从根结点到该结点的路径上的最大结点值。由于当前结点也在路径上,因此路径上的最大结点值一定不会小于当前结点值,如果当前结点值等于路径上的最大结点值,则当前结点是好结点。
在访问一个结点之后,对于其非空子结点,更新从根结点到子结点的路径上的最大结点值,对子结点继续遍历。
遍历结束之后,即可得到二叉树中好结点的数目。
代码
class Solution {
int count = 0;
public int goodNodes(TreeNode root) {
dfs(root, root.val);
return count;
}
public void dfs(TreeNode node, int maxValue) {
if (node.val == maxValue) {
count++;
}
TreeNode left = node.left, right = node.right;
if (left != null) {
int leftMaxValue = Math.max(left.val, maxValue);
dfs(left, leftMaxValue);
}
if (right != null) {
int rightMaxValue = Math.max(right.val, maxValue);
dfs(right, rightMaxValue);
}
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是栈空间,取决于二叉树的高度,最坏情况下二叉树的高度是 O ( n ) O(n) O(n)。
解法二
思路和算法
也可以使用广度优先搜索判断二叉树中的每个结点是否是好结点。从根结点开始遍历全部结点,访问结点的顺序为层数递增的顺序,遍历过程中,对于每个结点,得到该结点对应的从根结点到该结点的路径上的最大结点值,判断该结点是否是好结点。
为了记录每个结点对应的从根结点到该结点的路径上的最大结点值,需要使用两个队列,分别存储结点和路径上的最大结点值,这两个队列分别为结点队列和最大值队列,初始时将根结点和根结点值分别入结点队列和最大值队列。每次将一个结点和一个最大值分别从结点队列和最大值队列出队列,如果当前结点值等于路径上的最大结点值,则将好结点的数目加 1 1 1。对于当前结点的非空子结点,计算从根结点到子结点的路径上的最大结点值,然后将子结点和子结点对应的最大结点值分别入结点队列和最大值队列。
遍历结束之后,即可得到二叉树中好结点的数目。
代码
class Solution {
public int goodNodes(TreeNode root) {
int count = 0;
Queue<TreeNode> nodeQueue = new ArrayDeque<TreeNode>();
Queue<Integer> maxNode = new ArrayDeque<Integer>();
nodeQueue.offer(root);
maxNode.offer(root.val);
while (!nodeQueue.isEmpty()) {
TreeNode node = nodeQueue.poll();
int maxValue = maxNode.poll();
if (node.val == maxValue) {
count++;
}
TreeNode left = node.left, right = node.right;
if (left != null) {
nodeQueue.offer(left);
maxNode.offer(Math.max(left.val, maxValue));
}
if (right != null) {
nodeQueue.offer(right);
maxNode.offer(Math.max(right.val, maxValue));
}
}
return count;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。每个结点都被访问一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是二叉树的结点数。空间复杂度主要是队列空间,队列内元素个数不超过 n n n。