{x}
blog image

Find Leaves of Binary Tree

Solution Explanation:

This problem asks us to collect nodes of a binary tree level by level, starting from the leaves. The solution employs a Depth-First Search (DFS) approach to achieve this efficiently. The core idea is to determine the height of each node and assign it to a list corresponding to that height. Leaves will have height 0, their parents height 1, and so on.

Algorithm:

  1. Depth-First Search (DFS) with Height Calculation: A recursive dfs function traverses the tree. For each node:

    • If the node is null, it returns 0 (height of an empty subtree is 0).
    • It recursively calculates the height of the left and right subtrees (l and r).
    • The node's height (h) is the maximum of the left and right subtree heights plus 1.
    • If the ans list (which stores the results) doesn't have an element at index h, it adds a new list to represent that level.
    • The node's value is added to the list at index h in ans.
    • Finally, the node's height h is returned.
  2. Result Aggregation: The dfs function effectively assigns each node to a level based on its height. The ans list, which is initially empty, is populated during the DFS traversal. Each inner list within ans represents a level in the tree, containing the values of the nodes at that level.

Time and Space Complexity Analysis:

  • Time Complexity: O(N), where N is the number of nodes in the binary tree. This is because the DFS function visits each node exactly once.
  • Space Complexity: O(N) in the worst case. This is due to the recursive call stack of the DFS function, which could reach a depth of N in a skewed tree. Additionally, the ans list could store up to N nodes in the worst case (a complete binary tree).

Code Explanation (Python):

The Python code implements the algorithm described above:

class Solution:
    def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
        ans = []  # List to store the results (levels)
 
        def dfs(root: Optional[TreeNode]) -> int:
            if root is None:  # Base case: empty subtree
                return 0
            l, r = dfs(root.left), dfs(root.right)  # Recursive calls for subtrees
            h = max(l, r)  # Determine node's height
            if len(ans) == h:  # Add a new level if needed
                ans.append([])
            ans[h].append(root.val)  # Add node's value to its level
            return h + 1  # Return node's height
 
        dfs(root)  # Start DFS traversal from the root
        return ans

The other languages (Java, C++, Go, TypeScript, C#) follow the same logic, with only syntactic differences to adapt to the specific language's features. The core algorithm and complexity remain consistent across all implementations.