Binary Tree - DFS · Medium
LeetCode 1448: Count Good Nodes in Binary Tree — solution
Solve LeetCode 1448 with a DFS that carries the largest value seen on the root-to-node path — a node is good when its value is at least that maximum. O(n) time, O(h) space.
Problem in plain words
Walk from the root down to some node X. If no node on that walk (before X) has a value strictly larger than X, then X is called good. Count the good nodes in the tree. The root is always good, because nothing comes before it.
Example: root 5, left child 2, right child 7, and 7 has a left child 6. The good nodes are 5 (root), 7 (7 ≥ 5) — 2 is not good (5 > 2) and 6 is not good (7 > 6). The answer is 2.
Intuition
Whether X is good depends only on one number: the maximum value along the path from the root to X's parent. So pass that maximum down as you traverse. At each node, compare its value with the running maximum, then update the maximum before visiting the children. This is a top-down (pre-order) DFS where state flows from parent to child.
Approach
- Start a DFS at the root with
best = root.val. - At each node, if
node.val >= best, count it as good. - Update
best = max(best, node.val). - Recurse into the left and right children with the updated
best. - Return the total count.
Dry run
| Node | best on arrival | Good? | best passed to children |
|---|---|---|---|
| 5 | 5 | yes | 5 |
| 2 | 5 | no | 5 |
| 7 | 5 | yes | 7 |
| 6 | 7 | no | 7 |
Two nodes are good, so the answer is 2.
Java solution
class Solution {
public int goodNodes(TreeNode root) {
return count(root, root.val);
}
private int count(TreeNode node, int best) {
if (node == null) {
return 0;
}
int good = node.val >= best ? 1 : 0;
int nextBest = Math.max(best, node.val);
return good + count(node.left, nextBest) + count(node.right, nextBest);
}
}Python solution
class Solution:
def goodNodes(self, root: TreeNode) -> int:
count = 0
stack = [(root, root.val)]
while stack:
node, best = stack.pop()
if node.val >= best:
count += 1
best = max(best, node.val)
if node.left:
stack.append((node.left, best))
if node.right:
stack.append((node.right, best))
return countComplexity
| Measure | Cost | Why |
|---|---|---|
| Time | O(n) | Each node is processed once with O(1) work. |
| Space | O(h) | The recursion (or explicit stack) holds at most one path's worth of frames. |
Edge cases and common mistakes
- Using `>` instead of `>=` — a node equal to the path maximum is still good. Strict comparison undercounts.
- Sharing one global maximum — the maximum must be per path. Pass it as a parameter so the left and right branches do not affect each other.
- Initialising the maximum to 0 — values can be negative. Start from
root.val(or negative infinity). - Deep trees in Python — with up to 10^5 nodes a recursive solution can hit the recursion limit, which is why the Python version uses an explicit stack.
›Would BFS work too?
Yes. Store (node, best) pairs in a queue instead of a stack. The order of visiting does not matter, as long as each node carries the maximum of its own path.
›Why is the root always good?
The path from the root to itself has no earlier nodes, so nothing can be larger. Starting with best = root.val makes the code count it automatically.
