easyTreesPattern: DFS
Subtree Node Count Solution
Problem Statement
Given the root of a binary tree, return an array where each element at index `i` represents the total number of nodes in the subtree rooted at the `i`-th node (inclusive). The nodes are indexed level by level, from left to right. If the tree is empty, return an empty array.
Examples
Example 1:
Input:{"nodes":[1,2,3,4,5,6,7],"left":[1,3,5,-1,-1,-1,-1],"right":[2,4,6,-1,-1,7,-1]}
Output:[7,4,2,1,1,2,1]
Explanation: The tree nodes are: 1(root), 2(left of 1), 3(right of 1), 4(left of 2), 5(right of 2), 6(left of 3), 7(right of 3). Node 7 is the right child of node 3.
Node 1's subtree has 7 nodes.
Node 2's subtree has 4 nodes.
Node 3's subtree has 2 nodes.
Node 4's subtree has 1 node.
Node 5's subtree has 1 node.
Node 6's subtree has 2 nodes (itself and node 7).
Node 7's subtree has 1 node.
Example 2:
Input:{"nodes":[10,20],"left":[20,-1],"right":[-1,-1]}
Output:[2,1]
Explanation: Node 10 is the root. Node 20 is its left child.
Node 10's subtree has 2 nodes.
Node 20's subtree has 1 node.
Example 3:
Input:{"nodes":[],"left":[],"right":[]}
Output:[]
Explanation: An empty tree has no nodes, so the output is an empty array.
Constraints
- The number of nodes in the tree is between 0 and 1000.
- The value of each node is unique.
- The `left` and `right` arrays will have the same length as the `nodes` array.
- A value of -1 in `left` or `right` indicates no child.
Time: O(N) Space: O(N)
Utilize a single Depth First Search (DFS) traversal. For each node, recursively calculate the size of its left and right subtrees. The size of the current node's subtree is 1 plus the sum of its children's subtree sizes. Store these counts in an array that maps node indices to their subtree sizes.
Run, Test & Submit Code
Ready to practice this challenge? Launch our interactive compilation environment with compiler validation.
Solve on Interactive WorkspaceTested Solutions
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def subtreeNodeCount(root: TreeNode) -> list[int]:
if not root:
return []
# Helper function to build tree from example input format
def build_tree(nodes_data, left_data, right_data):
if not nodes_data:
return None
nodes = [None] * len(nodes_data)
node_val_to_obj = {}
for i, val in enumerate(nodes_data):
if val != -1:
nodes[i] = TreeNode(val)
node_val_to_obj[val] = nodes[i]
for i, node in enumerate(nodes):
if node:
# For simplicity, assuming indices in left_data and right_data correspond to indices in nodes_data
# This implies the input format needs careful handling based on level order
# The provided example structure needs to be mapped to actual tree connections.
# The problem statement implies level-order indexing, which is crucial.
# Let's assume the problem means node values map directly for simplicity in this correction.
# A more robust tree builder would parse level by level.
pass # The original code's build_tree logic was problematic for general cases
# A proper tree builder based on level-order indexing would be more complex.
# For the purpose of fixing the core logic, let's assume the root is passed and has correct structure.
return nodes[0] if nodes else None
# The core logic to count subtree nodes
node_to_index = {}
queue = [(root, 0)]
index_counter = 0
all_nodes_in_order = []
while queue:
current_node, current_index = queue.pop(0)
if current_node:
all_nodes_in_order.append(current_node.val)
node_to_index[current_node] = index_counter
index_counter += 1
# Enqueue children for level-order traversal to get indices
# This assumes the tree is already correctly built and we are mapping nodes to indices
queue.append((current_node.left, -1)) # Dummy index for children, will be re-indexed
queue.append((current_node.right, -1))
else:
# Handle None nodes if necessary for structure, but they don't contribute to count
pass
# Re-map to actual order of nodes encountered in a level-order traversal to get correct indices
actual_nodes_in_level_order = []
q_for_indices = [root]
while q_for_indices:
curr = q_for_indices.pop(0)
if curr:
actual_nodes_in_level_order.append(curr)
q_for_indices.append(curr.left)
q_for_indices.append(curr.right)
n = len(actual_nodes_in_level_order)
if n == 0: return []
# Create a mapping from node object to its index in the level-order list
node_obj_to_level_index = {node: i for i, node in enumerate(actual_nodes_in_level_order)}
subtree_counts = [0] * n
def dfs(node):
if not node:
return 0
left_count = dfs(node.left)
right_count = dfs(node.right)
current_count = 1 + left_count + right_count
# Get the index of the current node in the level-order traversal
current_node_level_index = node_obj_to_level_index[node]
subtree_counts[current_node_level_index] = current_count
return current_count
dfs(root)
return subtree_counts