Within a binary tree, a node x is considered good if the path from the root of the tree to the node x contains no nodes with a value greater than the value of node x
Given the root of a binary tree root, return the number of good nodes within the tree.
Example 1:
Input: root = [2,1,1,3,null,1,5]
Output: 3Example 2:
Input: root = [1,2,-1,3,4]
Output: 4Constraints:
1 <= number of nodes in the tree <= 100-100 <= Node.val <= 100
You should aim for a solution with O(n) time andO(n) space, where n is the number of nodes in the given tree.
A brute force solution would involve considering every node and checking if the path from the root to that node is valid, resulting in an O(n^2) time complexity. Can you think of a better approach?
We can use the Depth First Search (DFS) algorithm to traverse the tree. But can you think of a way to determine if the current node is a good node in a single traversal? Maybe we need to track a value while traversing the tree.
While traversing the tree, we should track the maximum value along the current path. This allows us to determine whether the nodes we encounter are good. We can use a global variable to count the number of good nodes.
Before attempting this problem, you should be comfortable with:
A node is “good” if on the path from the root to that node, no earlier node has a value greater than it.
So while traversing the tree, we just need to carry the maximum value seen so far on the current path.
If the current node’s value ≥ that maximum → it is a good node.
maxSoFar.node.val >= maxSoFar, count it as a good node.maxSoFar = max(maxSoFar, node.val).maxSoFarmaxSoFar# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def goodNodes(self, root: TreeNode) -> int:
def dfs(node, maxVal):
if not node:
return 0
res = 1 if node.val >= maxVal else 0
maxVal = max(maxVal, node.val)
res += dfs(node.left, maxVal)
res += dfs(node.right, maxVal)
return res
return dfs(root, root.val)A node is “good” if along the path from the root to that node, no earlier node has a value greater than it.
Using BFS, we can traverse level by level while carrying the maximum value seen so far for each path.
Whenever we visit a node, we compare its value with that max — if it's greater or equal, this node is good.
Each child inherits the updated maximum of its own path.
(node, maxSoFarOnPath).(root, -infinity) into the queue.(node, maxVal).node.val >= maxVal, increase the good-node count.(child, max(maxVal, node.val)) into the queue.# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def goodNodes(self, root: TreeNode) -> int:
res = 0
q = deque()
q.append((root,-float('inf')))
while q:
node,maxval = q.popleft()
if node.val >= maxval:
res += 1
if node.left:
q.append((node.left,max(maxval,node.val)))
if node.right:
q.append((node.right,max(maxval,node.val)))
return resA node is "good" if its value is greater than OR EQUAL to all ancestors. Using node.val > maxVal instead of node.val >= maxVal causes the root and equal-valued paths to be missed.
Starting with maxVal = 0 or maxVal = root.val can cause issues with negative values. Initialize with negative infinity or the root's value to correctly count the root as a good node.
# Wrong: misses root if root.val < 0
dfs(root, 0)
# Correct: root is always good
dfs(root, float('-inf'))The maximum value must be tracked per-path, not globally. Updating a shared variable instead of passing the new max to each recursive call causes incorrect comparisons across different branches.