Knowledge Guide
HomeDSATrees

medium Count Good Nodes in Binary Tree

Problem Statement

Given a binary tree root, return the number of good nodes in the binary tree.

A node is considered good if in the path from the root to this node, there are no nodes with a value greater than this node's value.

Examples

Example 1

Image
Image

Example 2

Image
Image

Example 3

Image
Image

Constraints:

Try it yourself

Try solving this question here:

✅ Solution Count Good Nodes in Binary Tree

Problem Statement

Given a binary tree root, return the number of good nodes in the binary tree.

A node is considered good if in the path from the root to this node, there are no nodes with a value greater than this node's value.

Examples

Example 1

  • Input: root = [3, 1, 3, 3, null, 1, 5]
Image
Image
  • Expected Output: 4
  • Explanation:
    • Root node (3) is always good.
    • Node 5 is also a good node.
    • Both nodes 3 are good.

Example 2

  • Input: root = [2, 3, 4, 1, null, null, 5]
Image
Image
  • Expected Output: 4
  • Explanation:
    • Nodes 2, 3, 4, and 5 are good.
    • The node with value 1 is not good because 3 is greater and comes before it.

Example 3

  • Input: root = [10, 8, 15, 7, 9, 12, 20]
Image
Image
  • Expected Output: 3
  • Explanation:
    • Nodes 10, 15, and 20 are good.

Constraints:

  • The number of nodes in the binary tree is in the range [1, 10^5].
  • Each node's value is between [-104, 10^4].

Solution

To solve this problem, we will use Depth-First Search (DFS) traversal. The idea is to traverse the tree starting from the root and keep track of the maximum value encountered on the path from the root to the current node. If the current node's value is greater than or equal to the maximum value seen so far, it is a good node. We then update the maximum value and continue to traverse the tree.

This approach works because it ensures that we only count a node as good if all nodes on the path from the root to that node have values less than or equal to the current node's value. By updating the maximum value dynamically as we traverse, we can efficiently determine if a node is good.

Step-by-step Algorithm

  1. Initialization:

    • Start with the root node of the tree.
  2. Define Helper Function (DFS):

    • Define a helper function dfs(node, maxVal) to perform depth-first search (DFS) on the tree. Initially, consider root node's value as a maxVal.
    • If the current node is null, return 0 because there are no nodes to consider.
  3. Check Good Node:

    • Check if the value of the current node is greater than or equal to maxVal.
    • If it is, this node is a good node. Initialize a variable count to 1.
    • Otherwise, set count to 0.
  4. Update Maximum Value:

    • Update maxVal to be the maximum of the current node's value and the previous maxVal.
  5. Recur for Subtrees:

    • Recursively call dfs for the left child of the current node with the updated maxVal and add the result to count.
    • Recursively call dfs for the right child of the current node with the updated maxVal and add the result to count.
  6. Return Count:

    • Return the total count of good nodes found in the current subtree.

Algorithm Walkthrough

Image
Image
  1. Initialization:

    • Start at the root node with value 2.
    • Initialize maxVal = 2.
  2. Step-by-Step DFS Traversal:

    • At Node 2:

      • maxVal = 2
      • 2 >= 2, so node 2 is a good node.
      • Count = 1
      • Update maxVal = max(2, 2) = 2
      • Recur for left child (Node 3).
    • At Node 3:

      • maxVal = 2
      • 3 >= 2, so node 3 is a good node.
      • Count = 1
      • Update maxVal = max(2, 3) = 3
      • Recur for left child (Node 1).
    • At Node 1:

      • maxVal = 3

      • 1 < 3, so node 1 is not a good node.

      • Count = 0

      • maxVal remains 3

      • Recur for left and right children (both are null), so return 0 for both.

      • Total count from Node 1's subtree: 0

    • Back at Node 3:

      • Count from left subtree (Node 1) = 0
      • Recur for right child (null), so return 0.
      • Total count from Node 3's subtree: 1 (Node 3 itself)
    • Back at Node 2:

      • Count from left subtree (Node 3) = 1
      • Recur for right child (Node 4).
    • At Node 4:

      • maxVal = 2
      • 4 >= 2, so node 4 is a good node.
      • Count = 1
      • Update maxVal = max(2, 4) = 4
      • Recur for left child (null), so return 0.
      • Recur for right child (Node 5).
    • At Node 5:

      • maxVal = 4

      • 5 >= 4, so node 5 is a good node.

      • Count = 1

      • Update maxVal = max(4, 5) = 5

      • Recur for left and right children (both are null), so return 0 for both.

      • Total count from Node 5's subtree: 1

    • Back at Node 4:

      • Count from right subtree (Node 5) = 1
      • Total count from Node 4's subtree: 2 (Nodes 4 and 5)
    • Back at Node 2:

      • Count from right subtree (Node 4) = 2
      • Total count from Node 2's subtree: 4 (Nodes 2, 3, 4, 5)
  3. Final Count:

    • The total count of good nodes in the tree is 4.

Code

java
// class TreeNode {
//     int val;
//     TreeNode left;
//     TreeNode right;
//     TreeNode(int x) { val = x; }
//     TreeNode(int x, TreeNode left, TreeNode right) {
//         val = x;
//         this.left = left;
//         this.right = right;
//     }
// }

class Solution {

  // Method to count good nodes
  public int goodNodes(TreeNode root) {
    return dfs(root, root.val);
  }

  // Helper method to perform DFS
  private int dfs(TreeNode node, int maxVal) {
    if (node == null) return 0;
    int count = node.val >= maxVal ? 1 : 0; // Check if current node is good
    maxVal = Math.max(maxVal, node.val); // Update max value on this path
    count += dfs(node.left, maxVal); // Recur for left child
    count += dfs(node.right, maxVal); // Recur for right child
    return count; // Return total count of good nodes
  }

  public static void main(String[] args) {
    Solution sol = new Solution();
    TreeNode root1 = new TreeNode(
      3,
      new TreeNode(1, new TreeNode(3), null),
      new TreeNode(4, new TreeNode(1), new TreeNode(5))
    );
    System.out.println(sol.goodNodes(root1)); // Expected output: 4

    TreeNode root2 = new TreeNode(
      2,
      new TreeNode(3, new TreeNode(1), null),
      new TreeNode(4, null, new TreeNode(5))
    );
    System.out.println(sol.goodNodes(root2)); // Expected output: 4

    TreeNode root3 = new TreeNode(
      10,
      new TreeNode(8, new TreeNode(7), new TreeNode(9)),
      new TreeNode(15, new TreeNode(12), new TreeNode(20))
    );
    System.out.println(sol.goodNodes(root3)); // Expected output: 3
  }
}

Complexity Analysis

Time Complexity

The time complexity of this algorithm is , where N is the number of nodes in the tree. This is because we visit each node exactly once during the DFS traversal.

Space Complexity

The space complexity is , where H is the height of the tree. This is due to the recursion stack used during the DFS traversal. In the worst case (for a skewed tree), H can be as large as N, leading to a space complexity of . For a balanced tree, the space complexity would be .

🤖 Don't fully get this? Learn it with Claude

Stuck on Count Good Nodes in Binary Tree? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Count Good Nodes in Binary Tree** (DSA). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Count Good Nodes in Binary Tree** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Count Good Nodes in Binary Tree**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Count Good Nodes in Binary Tree**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes