A node \( v \) in a binary tree is defined as properly balanced if the absolute difference between its left height and right height is at most 1, i.e.,
\[ |\text{LEFT-HEIGHT}(v) - \text{RIGHT-HEIGHT}(v)| \leq 1 \]
Our goal is to identify all such nodes efficiently, ideally in \( O(n) \) time, where \( n \) is the number of nodes in the tree.
Bottom-Up Height Computation
Since we need to determine the left and right heights for every node and check the balance condition, a naive approach—computing heights from scratch for each node—would take \( O(n) \) time per node, leading to an \( O(n^2) \) algorithm. 😕
To achieve efficiency, we can compute heights in a bottom-up manner using a post-order traversal. In a post-order traversal, we process a node’s left child, then its right child, and finally the node itself. This allows us to compute the height of a subtree once and reuse it when evaluating the parent node.😀
Algorithm Design and Plannig
We can design a recursive algorithm that:
1. Computes the height of each subtree rooted at a node.
2. Uses these heights to check the balance condition at each node.
3. Collects all nodes that are properly balanced. Simple 😉
NOTE: The height of a subtree rooted at a node is defined as the length of the longest path from that node to a leaf in its subtree, which is 1 plus the maximum of the left and right heights (or 0 if the node is a leaf or NULL). For a NULL node (no subtree), the height is 0, match with problem’s convention that the height is 0 when there is no child.
To enumerate the balanced nodes, we can accumulate them in a list during the traversal. We’ll use a recursive function that returns the height of the subtree and, as a side effect, appends properly balanced nodes to a shared list.
- Input: A binary tree \( T \) with a root node.
- Output: A list of all nodes in \( T \) that are properly balanced.
- Data Structure: A list `balanced_nodes` to store the properly balanced nodes, accessible across recursive calls.
Procedure
1. Initialize an empty list `balanced_nodes`.
2. Call `computeHeightAndCheckBalance(root, balanced_nodes)` recurssive function on the root of the tree \( T \).
- which use a post-order traversal to compute the heights of the left and right subtrees for each node.
- For each node, checking if the absolute difference between its left height and right height is less than or equal to 1.
- Collect the nodes that satisfy this condition in a list during the traversal.
3. After the call completes, `balanced_nodes` contains all properly balanced nodes.
4. Return or process `balanced_nodes` as needed
// Recursive function to compute height and check balance
int computeHeightAndCheckBalance(node, balanced_nodes)
{
// Base case: empty subtree
if (node == NULL) {
return 0
}
// Recursively compute left height
left_height = computeHeightAndCheckBalance(node->left, balanced_nodes)
// Recursively compute right height
right_height = computeHeightAndCheckBalance(node->right, balanced_nodes)
// Check if the node is properly balanced
if (abs(left_height - right_height) <= 1) {
addBalancedNode(balanced_nodes, node)
}
// Compute the height of the current node
height = 1 + (left_height > right_height ? left_height : right_height)
return height
}
// Add a node to the balanced nodes list
void addBalancedNode(list, node)
{
if (list->count >= list->capacity)
{
// Simple handling: in practice, resize the array
print("Balanced nodes array is full!\n")
return;
}
list->nodes[list->count] = node;
list->count++;
}
Complexity Analysis
Time Complexity: The algorithm performs a post-order traversal, visiting each node exactly once. At each node, it performs constant-time operations: two recursive calls (processed once per node), a balance check, and possibly appending to the list. Thus, the total time is \( O(n) \), where \( n \) is the number of nodes.
Space Complexity: The recursion stack depth is equal to the height of the tree, which is \( O(h) \), where \( h \) is the tree’s height. In the worst case (e.g., a skewed tree), \( h = n \), so \( O(n) \).
- The `balanced_nodes` list stores up to \( n \) nodes, requiring \( O(n) \) space.
- Total space complexity is \( O(n) \).