Pattern 07 of 20

Trees

Recursion on subtrees: depth-first for paths and properties, breadth-first for levels.

When to use it

  • The input is a binary tree or a binary search tree
  • The answer combines results from the left and right subtrees
  • You need nodes level by level

The idea

Solve the problem for one node by assuming it is solved for its children, then combine. Decide what each call returns to its parent and what it records globally; that choice is most of the solution.

Typical cost O(n) time, O(h) stack space where h is the tree height.

How it works

Tree problems are recursion problems. Assume your function already works on the left and right subtrees, then decide how to combine their answers at the current node. If you can state what one call returns to its parent, the code nearly writes itself.

Many questions need two quantities: the value a node reports upward and a global best that may be achieved at that node. Diameter and maximum path sum are the classic cases: each node returns its best downward path, but the answer is the best path that bends through it.

Binary search trees add an ordering invariant. An in-order traversal visits values in sorted order, and every node carries an allowed (low, high) range from its ancestors. Validation, k-th smallest and lowest common ancestor all exploit that.

Breadth-first search is the tool for anything about levels: level averages, right-side view, minimum depth, zigzag order. Process the queue one level at a time by reading its length before the inner loop.

How to attack it

  1. 1Decide depth-first (paths, subtree properties) or breadth-first (levels, nearest).
  2. 2Define exactly what the recursive call returns for a subtree.
  3. 3Write the base case for an empty node first.
  4. 4Decide whether a separate global answer is needed beyond the return value.
  5. 5Consider recursion depth on a skewed tree and switch to an explicit stack if needed.

Template

A skeleton to adapt, not a solution to paste. Write it from memory a few times.

from collections import deque


def max_depth(root):
    # Post-order: answer for a node from its children's answers.
    if root is None:
        return 0
    return 1 + max(max_depth(root.left), max_depth(root.right))


def diameter(root):
    best = 0
    def height(node):
        nonlocal best
        if node is None:
            return 0
        l, r = height(node.left), height(node.right)
        best = max(best, l + r)   # global answer: a path bending at node
        return 1 + max(l, r)      # value reported to the parent
    height(root)
    return best


def level_order(root):
    levels, queue = [], deque([root] if root else [])
    while queue:
        level = []
        for _ in range(len(queue)):   # exactly one level per outer loop
            node = queue.popleft()
            level.append(node.val)
            queue.extend(c for c in (node.left, node.right) if c)
        levels.append(level)
    return levels

Variants

Post-order aggregation
Height, size, balance and subtree sums: compute children first, then the node.
Return value plus global best
Diameter and maximum path sum report one thing upward and update a separate best at each node.
Top-down with state
Pass a running path sum, depth or allowed BST range down to the children.
Level-order BFS
Snapshot the queue length (or swap queues) to handle one level at a time.
Construction and serialization
Rebuild a tree from two traversals, or encode it with null markers; preorder with markers is unambiguous.

Pitfalls

  • Validating a BST by comparing each node only with its direct children instead of its full ancestor range.
  • Confusing what the function returns with the global answer, especially in path problems.
  • Not handling the empty tree at the top level.
  • Using queue.shift() in JavaScript inside a hot loop, which is O(n) per call.
  • Stack overflow from recursion on a degenerate tree of 10^5 nodes.

What it costs

OperationCost
DFS or BFS over all nodesO(n) time
Recursive DFS stackO(h): O(log n) balanced, O(n) skewed
BFS queueO(w), the widest level, up to n/2
BST search / insertO(h)

61 Trees problems

Easiest first. Problems marked Free can be solved without Pro.

  1. 1Tree HeightFreeEasy
  2. 2Root-First Visit OrderFreeEasy
  3. 3Left-Root-Right ReadingFreeEasy
  4. 4Children Before ParentEasy
  5. 5Twin TreesEasy
  6. 6Mirror-Image CheckEasy
  7. 7Even-Keeled TreeEasy
  8. 8Embedded CopyEasy
  9. 9Root-to-Leaf Target TotalEasy
  10. 10Left-Hanging LeavesEasy
  11. 11Flip Every BranchEasy
  12. 12Locate a Key in a Search TreeEasy
  13. 13Nearest Shelf LabelEasy
  14. 14Price Band TotalEasy
  15. 15Balanced Tree From Sorted PricesEasy
  16. 16Smallest Gap Between LabelsEasy
  17. 17Overlay Two TreesEasy
  18. 18Two Keys That Add UpEasy
  19. 19Flatten a Search Tree Into a ChainEasy
  20. 20Most Frequent Search KeysEasy
  21. 21Floor-by-Floor ListingMedium
  22. 22Serpentine Floor WalkMedium
  23. 23Skyline From the EastMedium
  24. 24Deepest Leftmost ValueMedium
  25. 25Longest Span Between NodesMedium
  26. 26Widest FloorMedium
  27. 27Outline of the TreeMedium
  28. 28Digit Trails TotalMedium
  29. 29Every Trail to the TargetMedium
  30. 30Unchallenged NodesMedium
  31. 31Downward Segments With Target SumMedium
  32. 32Longest Uniform ChainMedium
  33. 33Tree HeistMedium
  34. 34Coin Balancing MovesMedium
  35. 35Spread From the SparkMedium
  36. 36Is It a Search Tree?Medium
  37. 37K-th Ranked TicketMedium
  38. 38Shelve a New KeyMedium
  39. 39Remove a Catalog EntryMedium
  40. 40Trim a Search Tree to a WindowMedium
  41. 41Shared Ancestor in a Search TreeMedium
  42. 42Add Every Larger KeyMedium
  43. 43Next Larger KeyMedium
  44. 44Repair Two Swapped KeysMedium
  45. 45Rebuild From Pre-Order and In-Order LogsMedium
  46. 46Rebuild From In-Order and Post-Order LogsMedium
  47. 47Search Tree From a Pre-Order LogMedium
  48. 48Nearest Common ManagerMedium
  49. 49Encode a Tree as TextMedium
  50. 50Decode a Tree From TextMedium
  51. 51Nodes Exactly K Hops AwayMedium
  52. 52Column-by-Column ReportHard
  53. 53Heaviest Path AnywhereHard
  54. 54Fewest WatchtowersHard
  55. 55Height After PruningHard
  56. 56Total Distance From Every NodeHard
  57. 57Richest Valid Search SubtreeHard
  58. 58Insertion Orders for the Same TreeHard
  59. 59Fewest Cameras to Watch Every NodeHard
  60. 60Rebuild a Tree From a Dashed OutlineHard
  61. 61Height After Cutting a BranchHard

Solve them in the browser, in Python or JavaScript.