#1448 Count Good Nodes in Binary Tree
the problem
In plain words
Call a node good if no node on the path from the root down to it has a bigger value. Count the good nodes. The root is always good.
in root = [3,1,4,3,null,1,5]
out 4
The root 3, then 4, 5 and the lower 3 are good; the two 1s are not.
in root = [3,3,null,4,2]
out 3
2 is not good: a 3 sits above it.
in root = [1]
out 1
- 1 ≤ number of nodes ≤ 10⁵
- -10⁴ ≤ Node.val ≤ 10⁴
your turn
Try it before you peek
Read the examples, say the brute force out loud, then write code: here or on LeetCode ↗. Stuck for 20 minutes? Open one hint at a time.
Solve it in the browser
LeetCode-style: complete the class. Compiled by the real javac (Java 17) and judged right here — nothing is sent anywhere.
Solutions are hidden so you can try first. Even 20 minutes of real effort makes the answer stick far better than reading it.
Marking the problem solved, or opening every hint, also shows it.
the aha
Key insight
patternTree DFS (top-down)
A node needs only one fact about everything above it: the biggest value so far. Pass that max down as a parameter, and add up the good counts on the way back up.
solutions
From brute force to optimal
Keep the root-to-node path in a list; at every node, scan the list for a bigger value.
- Good if no value in path is bigger than node.val.
- path.add(node.val), recurse into both children, path.remove(last).
Each node scans up to h ancestors: O(n log n) when balanced, O(n²) for a chain.
The path list and the recursion stack.
class Solution { public int goodNodes(TreeNode root) { return count(root, new ArrayList<>()); } /** path holds the values from the root down to node's parent. */ private int count(TreeNode node, List<Integer> path) { if (node == null) return 0; boolean good = true; for (int above : path) { // re-check every ancestor, every time if (above > node.val) { good = false; break; } } path.add(node.val); int total = (good ? 1 : 0) + count(node.left, path) + count(node.right, path); path.remove(path.size() - 1); return total; }}count(node, maxAbove) = good nodes in this subtree, given the largest value above it.
- null → 0.
- good = node.val >= maxAbove ? 1 : 0.
- max = Math.max(maxAbove, node.val).
- Return good + count(left, max) + count(right, max). Start with Integer.MIN_VALUE.
One visit per node, O(1) work each.
Recursion stack: O(log n) when balanced, up to O(n) for a skewed tree.
class Solution { public int goodNodes(TreeNode root) { return count(root, Integer.MIN_VALUE); // nothing is above the root } /** Good nodes in this subtree, given the largest value on the path above it. */ private int count(TreeNode node, int maxAbove) { if (node == null) return 0; int good = node.val >= maxAbove ? 1 : 0; // nothing above is bigger int max = Math.max(maxAbove, node.val); // the max my children will see return good + count(node.left, max) + count(node.right, max); }}Dry run
The same state, carried on an explicit ArrayDeque instead of the call stack.
- Push (root, MIN_VALUE).
- Pop; count it if node.val >= max.
- Push each child with Math.max(max, node.val).
Each node is pushed and popped once.
At most one waiting sibling per level — on the heap, so a 10⁵-deep chain is safe.
class Solution { public int goodNodes(TreeNode root) { if (root == null) return 0; Deque<TreeNode> nodes = new ArrayDeque<>(); Deque<Integer> maxAbove = new ArrayDeque<>(); // the largest value above the node at the same position nodes.push(root); maxAbove.push(Integer.MIN_VALUE); int good = 0; while (!nodes.isEmpty()) { TreeNode node = nodes.pop(); int max = maxAbove.pop(); if (node.val >= max) good++; int next = Math.max(max, node.val); if (node.left != null) { nodes.push(node.left); maxAbove.push(next); } if (node.right != null) { nodes.push(node.right); maxAbove.push(next); } } return good; }}in the room
How to say it in the interview
talk track
- Name the state: "each node only needs the max value on the path above it."
- Pass it down as a parameter; return the count up. The root starts with Integer.MIN_VALUE (or its own value).
- n can be 10⁵: mention that a chain that deep needs the iterative version in Java.
traps
- Comparing only with the parent: in [1,10000,null,5,null,9999], 9999 beats its parent 5 but not the 10000 above it.
- Using
>instead of>=: a node equal to the max so far is still good. - Starting the max at 0: with negative values the root would wrongly count as bad. Start at Integer.MIN_VALUE.
what they ask next
Follow-ups
Return the good nodes, not just how many?
The same walk; add the node to a list whenever it is good.
Largest difference between a node and one of its ancestors (LC 1026)?
Pass both the min and the max seen so far down the path; at each node the best difference uses one of the two.
before you submit
Edge cases to test
- One node → 1.
- Ties count: [5,5,5] → 3.
- A decreasing chain → only the root is good; an increasing chain → every node is good.
practise next
Similar problems
- #129Sum Root to Leaf NumbersMediumin Tree Recursion
- #112Path SumEasyin Tree Recursion
- #98Validate Binary Search TreeMediumin BST