◀ THE GRIND — TREES

House Robber III

MEDIUM✓ CHIP-TIMEDLC #337 — FULL STATEMENT ↗

The drill: Every house sits in a binary tree, and robbing a house auto-alerts its direct parent and children — pick the take-free subset of houses worth the most, tree-shaped instead of a line.

THE BRIEFING — THE FULL DRILL, IN MY OWN WORDS

Houses sit as nodes in a binary tree, each holding some amount of loot, and robbing any house instantly alerts its direct parent and its direct children.

Two houses connected by a single edge can never both be robbed on the same night, but two houses two edges apart are perfectly safe together — the ban only covers immediate parent-child pairs.

The goal is picking the subset of houses that avoids every such adjacent pair while banking the largest possible total, and returning that maximum total.

EX 01
root = []
0
EMPTY TREE
EX 02
root = [1]
1
SINGLE NODE
EX 03
root = [5, 1, 1]
5
ROOT BEATS LEAVES
THE HINTS — TAKE ONLY WHAT YOU NEED
HINT 1 THE NUDGE

At every node you're really choosing between two totals: rob this house (its children are off-limits) or skip it (its children are fair game). Which total propagates upward depends on what the parent decides.

HINT 2 THE STRUCTURE

A node can't know whether to rob itself until it knows both outcomes for each child — robbed and not-robbed — not just one merged number.

HINT 3 ONE STEP FROM THE ANSWER

Postorder DFS returning a pair (robThis, skipThis): robThis = node.val + both children's skip values; skipThis = sum of max(rob, skip) per child. The answer is max of the pair at the root.

COACH'S BOARD — THE PATTERN, STEP BY STEP
ROB OR SKIPPATTERN · ROB/SKIP PAIR, ONE PASSroot = [4, 1, 5, null, 2, null, 3]
STEP 1

Robbing a house bans its direct parent and children only. Postorder gives each node both outcomes before its parent needs them.

STEP 1 / 7 · ← → WORK TOO
THE SPLITS — TWO PACES, TWO LANGUAGES
grind/house-robber-iii.pyRACE PACE
LANG ▸
PACE ▸
class Solution:
    def rob(self, root: Optional[TreeNode]) -> int:
        def dfs(node: Optional[TreeNode]):
            if not node:
                return (0, 0)              # (rob this, skip this)
            rob_l, skip_l = dfs(node.left)
            rob_r, skip_r = dfs(node.right)
            rob_this = node.val + skip_l + skip_r
            skip_this = max(rob_l, skip_l) + max(rob_r, skip_r)
            return (rob_this, skip_this)

        return max(dfs(root))
TIME O(N)SPACE O(H)PYTHON · RACE PACE · 12 LN

✓ CHIP-TIMED — ALL 4 SOLUTIONS RAN GREEN AGAINST SELF-AUTHORED CASES IN CI · JDK 21 · CPYTHON 3.12 · NOTHING PUBLISHES RED