Prerequisites
Before attempting this problem, you should be comfortable with:
- Binary Tree Traversal - Understanding how to navigate through tree nodes recursively
- Depth-First Search (DFS) - Used to compute the height of each node from the bottom up
- Tree Height Calculation - Understanding the concept of node height (distance to farthest leaf descendant)
1. DFS (Depth-First Search) with sorting
Intuition
Leaves are nodes with no children, and they have height 0. Their parents have height 1, and so on. If we compute the height of each node (where height is the distance to the farthest leaf in its subtree), nodes with the same height should be grouped together. By collecting all (height, value) pairs and sorting by height, we can form the required groups.
Algorithm
- Perform a DFS to compute the height of each node.
- For each node, calculate its height as
max(leftHeight, rightHeight) + 1, where leaf nodes return-1fromnullchildren. - Store each (height, value) pair in a list.
- Sort the list by height.
- Group nodes with the same height into sublists and return the result.
class Solution:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
self.pairs = []
def getHeight(node):
if node is None:
return -1
# first calculate the height of the left and right children
leftHeight = getHeight(node.left)
rightHeight = getHeight(node.right)
# based on the height of the left and right children, obtain the height of the current (parent) node
currHeight = max(leftHeight, rightHeight) + 1
# collect the pair -> (height, val)
self.pairs.append((currHeight, node.val))
# return the height of the current node
return currHeight
getHeight(root)
# sort all the (height, val) pairs
self.pairs.sort(key=lambda p: p[0])
n = len(self.pairs)
height = 0
i = 0
solution = []
while i < n:
nums = []
while i < n and self.pairs[i][0] == height:
nums.append(self.pairs[i][1])
i += 1
solution.append(nums)
height += 1
return solutionTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the total number of nodes in the binary tree
2. DFS (Depth-First Search) without sorting
Intuition
Instead of collecting pairs and sorting, we can directly place each node into the correct group during the DFS. When we compute a node's height, we use that height as an index into the result list. If the list doesn't have enough sublists yet, we create a new one.
Algorithm
- Initialize an empty result list.
- Perform a DFS to compute the height of each node:
- If the node is
null, return-1. - Recursively get the heights of left and right children.
- Compute current height as
max(leftHeight, rightHeight) + 1. - If the result list size equals the current height, append a new empty sublist.
- Add the node's value to
result[currentHeight]. - Return the current height.
- If the node is
- Call DFS on the root and return the result.
class Solution:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
self.solution = []
def getHeight(node):
if node is None:
return -1
leftHeight = getHeight(node.left)
rightHeight = getHeight(node.right)
currHeight = max(leftHeight, rightHeight) + 1
if len(self.solution) == currHeight:
self.solution.append([])
self.solution[currHeight].append(node.val)
return currHeight
getHeight(root)
return self.solutionTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the total number of nodes in the binary tree
Common Pitfalls
Confusing Height with Depth
Height is measured from the node down to its farthest leaf (leaves have height 0), while depth is measured from the root down to the node. This problem requires height-based grouping, not depth. Using depth would group nodes by their distance from the root, which produces incorrect results since leaves at different depths would end up in different groups.
Returning Wrong Base Case Value
The base case for null nodes should return -1, not 0. Since leaves have height 0 (computed as max(-1, -1) + 1), returning 0 for null would incorrectly make leaves have height 1. This off-by-one error shifts all groupings and produces wrong output.
Forgetting to Handle Result List Growth
When directly inserting nodes into the result list by height index, you must check if the list has enough sublists before accessing result[height]. Failing to add a new sublist when result.size() == height causes an index out of bounds error. Always ensure the result list grows dynamically as you encounter nodes with new height values.