Coding Interview Patterns Module 11 – Tree patterns: depth-first
Maximum path sum through any node
Find the largest path sum in a binary tree, a chain of linked shops with any two ends: the problem, a brute force over every pair, and what to notice.
What you will learn
- Distinguish the value a call returns to its parent from the best value it records
- Remove branches at a loss with max(0, child), and start the best below every possible total
- Prove that every path is counted once, at its highest node
- Build the best path itself, and adapt the pass to paths between two leaves and to nodes with many children
Before you start
What you’ll learn · Before you start
What you will learn
- Distinguish the value a call returns to its parent from the best value it records
- Remove branches at a loss with max(0, child), and start the best below every possible total
- Prove that every path is counted once, at its highest node
- Build the best path itself, and adapt the pass to paths between two leaves and to nodes with many children
Before you start
On this page
The maximum path sum of a binary tree is the largest total of the values on a path between any two nodes, where a path follows the edges, uses each node at most once and may bend at one node. Values can be negative, so the best path may skip the root, stop early or be a single node. The obvious solution adds up every path between every pair of nodes, O(n²) at least; one bottom-up pass is enough, once each call keeps apart the value it returns to its parent and the best value it records.
The most profitable chain of shops
A market’s shops are linked as a binary tree: the entrance at the root, and every shop linked to at most two shops below it. Each shop has a profit, a whole number that is negative for a shop at a loss. A chain is a path of linked shops between any two shops, each shop at most once, with at least one shop.
Return the largest total profit of a chain. Limits: 1 ≤ n ≤ 10⁵ shops, at most 900 levels, profits from −10⁴ to 10⁴, so a total stays within ±10⁹.
- Market A,
[-6, 8, 2, 4, 7, None, 3, 6]in level order: 25, the chain 6, 4, 8, 7. It bends at the 8 and leaves the entrance out, which would cost 6. - Market B,
[-3, -7, -2], every shop at a loss: −2, the single best shop. - A market of one shop with a profit of 5: 5.
Market A, with its most profitable chain
Text description of the diagram
The diagram shows market A as a binary tree of seven shops, each labelled with its profit. Each link is marked L or R for the side it leaves from.
- The entrance, at the top, has a profit of -6. Below it are a shop with 8 on the left and a shop with 2 on the right.
- Under the 8: a shop with 4 on the left, which has a shop with 6 below it on the left, and a shop with 7 on the right.
- Under the 2: a shop with 3 on the right.
The most profitable chain is 6, 4, 8, 7, with a total of 25. Its links are drawn solid and thick, and its top shop, the 8, has a double border. The other links, from the entrance and from the 2, are dashed. Adding the entrance would cost 6, so the best chain leaves it out.
The brute force: a walk from every shop
Start at every shop and walk the links in every direction, up and down, adding the profits: every chain that starts at that shop is seen once, and the best total over all walks is the answer. Each walk visits all n shops, so the work is exactly n² steps.
free/brute_force.py
"""The brute force: start a walk at every shop, follow the links in every direction, and add up the profits of every
chain that starts there. Every step of every walk is counted."""
import random
from tree import build, random_tree
steps = 0
def best_chain_brute(root):
"""The largest total profit of a chain of linked shops, any two ends, at least one shop."""
global steps
shops, links, queue = [], {}, [root] if root else []
for shop in queue: # list every shop and its links, up and down, without recursion
shops.append(shop)
links.setdefault(shop, [])
for child in (shop.left, shop.right):
if child is not None:
links[shop].append(child)
links[child] = [shop]
queue.append(child)
best = float("-inf")
for start in shops:
walk = [(start, None, start.val)] # (shop, the shop we came from, profit of the chain so far)
while walk:
shop, came_from, total = walk.pop()
steps += 1
best = max(best, total) # the chain from start to this shop
walk += [(nxt, shop, total + nxt.val) for nxt in links[shop] if nxt is not came_from]
return best
markets = {
"market A": build([-6, 8, 2, 4, 7, None, 3, 6]),
"market B, every shop at a loss": build([-3, -7, -2]),
"one shop": build([5]),
}
for name, root in markets.items():
steps = 0
best = best_chain_brute(root)
print(f"{name}: best chain {best} ({steps} step{'' if steps == 1 else 's'})")
rng = random.Random(1)
for n in (500, 1_000, 2_000):
steps = 0
best_chain_brute(random_tree(rng, n))
print(f"random market of {n:,} shops: {steps:,} steps, n squared is {n * n:,}") Output
market A: best chain 25 (49 steps) market B, every shop at a loss: best chain -2 (9 steps) one shop: best chain 5 (1 step) random market of 500 shops: 250,000 steps, n squared is 250,000 random market of 1,000 shops: 1,000,000 steps, n squared is 1,000,000 random market of 2,000 shops: 4,000,000 steps, n squared is 4,000,000
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 brute_force.py
free/tree.py
"""Test markets for this lesson: build() reads a level-order list of shop profits, and random_tree() makes markets of
every shape. Neither uses recursion, so they work for any depth."""
from collections import deque
class Node:
__slots__ = ("val", "left", "right")
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def build(values):
"""The tree of a level-order list: values row by row, None where a child is missing."""
if not values or values[0] is None:
return None
root = Node(values[0])
waiting = deque([root])
i = 1
while i < len(values):
parent = waiting.popleft()
if values[i] is not None:
parent.left = Node(values[i])
waiting.append(parent.left)
i += 1
if i < len(values) and values[i] is not None:
parent.right = Node(values[i])
waiting.append(parent.right)
i += 1
return root
def random_tree(rng, size, low=-10, high=10):
"""A market of `size` shops with profits from low to high: each new shop goes into a random empty place."""
if size == 0:
return None
root = Node(rng.randint(low, high))
places = [(root, "left"), (root, "right")]
for _ in range(size - 1):
parent, side = places.pop(rng.randrange(len(places)))
child = Node(rng.randint(low, high))
setattr(parent, side, child)
places += [(child, "left"), (child, "right")]
return root Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The step counts are n² on the nose: 4,000,000 for 2,000 shops. At the limit of 10⁵ shops that is 10¹⁰ steps, over half an hour of Python at the recorded speed and well over a minute of compiled code, for a question about 10⁵ numbers.
Python Online Compiler Paste brute_force.py and tree.py, then try a market of your own, with every shop at a loss.What to notice
- Every chain has one top shop, the one nearest the entrance. The chain is that shop, plus at most one chain going down from its left link and at most one going down from its right link.
- A chain going down from a shop is what its parent can extend; a chain that bends at the shop cannot be extended any further.
- A side whose best downward chain is at a loss only lowers the total, so it is better left out.
Return the best chain going down, record the best bend Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why every chain is counted, once, at its top shop
- A dry run on market A
- The code in Python, JavaScript, Java, C++ and Go
- Measured: one pass against the brute force
- Edge cases and six common bugs
- Variants: the chain itself, two end shops, many links
- Exercise: the most profitable chain of linked shops
- The most profitable chain of linked shops
- A practice set with answers
- Check yourself
- Follow-up questions, answered
- Interview questions
- In the maximum path sum problem, each recursive call returns one number and records another. What are the two, why must they differ, and what goes wrong if a call returns the value it records?
- Your maximum path sum solution passes every test except one in which every value is negative, where it returns 0. Find the two most likely causes, fix them, and explain why max(0, child) is still right for the children.
- Change the maximum path sum so that both ends of the path must be leaves. Which parts of the usual solution break, how do you handle a node with one child, and what do you return when the tree has fewer than two leaves?
- Key takeaways
7 runnable examples (C++, Go, Java, JavaScript, Python, drills.py, gain.py, tree.py, variants.py) · 1 exercise with sample tests · 3 interview answers · about 20 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- NIST Dictionary of Algorithms and Data Structures: path (National Institute of Standards and Technology (NIST))
- math.inf and float("-inf") (The Python Standard Library: math) (Python Software Foundation)
- The nonlocal statement (The Python Language Reference) (Python Software Foundation)
- Java SE 25 API: Long.MIN_VALUE (Oracle)
- The Java Language Specification, Java SE 25: 15.18.2 Additive operators (+ and -) for numeric types (Oracle)
- std::numeric_limits (cppreference.com) (cppreference.com)
- Package math: MinInt64 (The Go Authors)
- ECMAScript 2026 Language Specification: Number.NEGATIVE_INFINITY (Ecma International)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress