Logo for ammarahmed.ca
Medium
Feb 19, 2026
#binary-tree
#dp

337. House Robber III

The thief has found himself a new place for his thievery again. There is only one entrance to this area, called root.

Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that all houses in this place form a binary tree. It will automatically contact the police if two directly-linked houses were broken into on the same night.

Given the root of the binary tree, return the maximum amount of money the thief can rob without alerting the police.

Example 1:

1Input: root = [3,2,3,null,3,null,1] 2Output: 7 3Explanation: Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.

Example 2:

1Input: root = [3,4,5,1,3,null,1] 2Output: 9 3Explanation: Maximum amount of money the thief can rob = 4 + 5 = 9.
  • The number of nodes in the tree is in the range [1, 104].
    • 0 <= Node.val <= 104

      Constraints:

      Notes

      Intuition

      We want to calculate the max amount the robber can take if they decide to steal from each house or to skip that house. Stealing would necessitate skipping over the direct children, and in the case of skipping we don't care whether we skip or take the child, we just want the max.

      Implementation

      In order to process nodes only once, we'll initialize a hashmap for the node stored with its take and skip value. Define the recursive DFS function, the base case checks if the node exists in the hashmap and early returns. Otherwise, we initialize a take value and skip value for the current node. The take value is initialized to the node's value and the skip value is initialized to zero. Then we iterate over each of the children and call the function recursively for each child. We add the skip value for the child to the take value for the current node to represent that the child is skipped when we decide to take the node. For the skip value of the current node we add the max of the take and skip value for the child because we don't care whether we skip the child or take it, we just want the max.

      After iterating over the children, we save the node and the take and skip values in the hashmap to ensure processing only once and return them.

      Finally, we call the function on the root node and take the max of the values.

      • Time: O(n), we iterate through each node once.
        • Space: O(n), the hashmap will contain all of the nodes as well as the call stack

          Complexity

          Solution

          1from typing import Optional, Tuple 2from lcutils import TreeNode 3 4# Definition for a binary tree node. 5# class TreeNode: 6# def __init__(self, val=0, left=None, right=None): 7# self.val = val 8# self.left = left 9# self.right = right 10class Solution: 11 def rob(self, root: Optional[TreeNode]) -> int: 12 if not root: 13 return 0 14 dp = {} 15 def dfs(node: TreeNode) -> Tuple[int, int]: 16 if node in dp: 17 return dp[node] 18 take_n = node.val 19 skip_n = 0 20 children = [node.left, node.right] 21 for child in children: 22 if not child: 23 continue 24 take, skip = dfs(child) 25 take_n += skip # if we decided to take the current node, direct children should be skipped 26 skip_n += max(take, skip) # if we skip the current node, we don't care whether we take or skip the child, just want the max 27 28 dp[node] = (take_n, skip_n) 29 return dp[node] 30 31 take, skip = dfs(root) 32 return max(take, skip) 33 34 35 36if __name__ == "__main__": 37 # Include one-off tests here or debugging logic that can be run by running this file 38 # e.g. print(solution.two_sum([1, 2, 3, 4], 3)) 39 solution = Solution() 40