You are given the root of a binary tree. You need to find and return the leaves of the tree in a specific order. The order is determined by repeatedly removing the leaves from the tree until the tree is empty. Each time you remove the leaves, you collect them into a list. The final result should be a list of lists, where each inner list contains the leaves removed in one iteration. The original tree is modified during this operation.
For example, consider the following binary tree:
1
/ \
2 3
/ \
4 5
The steps to find the leaves are:
4
, 5
, and 3
. Result: [[4, 5, 3]]
1
/
2
2
. Result: [[4, 5, 3], [2]]
1
1
. Result: [[4, 5, 3], [2], [1]]
The final answer should be [[4, 5, 3], [2], [1]]
.
Can you implement a function that takes the root of a binary tree and returns a list of lists representing the leaves removed in each iteration?
The most straightforward approach is to repeatedly identify and remove the leaves from the tree until the tree is empty. In each iteration, we traverse the tree and collect all the leaf nodes. After collecting them, we remove them from the tree. We repeat this process until the root is null. This will modify the original tree.
Algorithm:
result
, to store the leaves at each level.null
:
leaves
, to store the leaves for the current level.leaves
list. A leaf node is a node whose left and right children are both null
.leaves
list to the result
list.result
list.Code (Python):
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def findLeavesNaive(root):
result = []
while root:
leaves = []
new_root = removeLeaves(root, leaves)
result.append(leaves)
root = new_root
return result
def removeLeaves(root, leaves):
if not root:
return None
if not root.left and not root.right:
leaves.append(root.val)
return None
root.left = removeLeaves(root.left, leaves)
root.right = removeLeaves(root.right, leaves)
return root
Time Complexity: O(N^2) in the worst case, where N is the number of nodes. Removing each leaf takes O(N) and this process is repeated until the tree is empty. In the worst case (e.g. a skewed tree) each removal operation takes O(N).
Space Complexity: O(N) due to the recursion stack. It could be O(log N) for balanced tree. O(N) to store the result in the worst case.
The optimal approach leverages the height of each node in the tree. The key idea is that leaves are nodes with height 0. The root of the tree will be the last to be removed, so it will have the maximum height. The height can be defined recursively.
Algorithm:
result
, to store the leaves at each level.getHeight(node)
that does the following:
null
, return -1. (Important: -1 as null node has height -1.)result
list does not have an element at the index corresponding to the height of the current node, add a new empty list to result
.result
list.getHeight(root)
function.result
list.Code (Python):
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def findLeaves(root):
result = []
def getHeight(node):
if not node:
return -1
left_height = getHeight(node.left)
right_height = getHeight(node.right)
height = max(left_height, right_height) + 1
if len(result) <= height:
result.append([])
result[height].append(node.val)
return height
getHeight(root)
return result
Time Complexity: O(N), where N is the number of nodes in the tree. We visit each node exactly once.
Space Complexity: O(N), where N is the number of nodes in the tree. This is due to the recursion stack in the worst-case scenario (skewed tree). The result
list can also store up to N node values in the worst case.
null
), the algorithm should return an empty list.