Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Advanced
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 9 of 35 minutes free
Free: The most profitable chain of shops · The brute force: a walk from every shop · What to notice. With a pass: the other 7 sections and 3 interview answers, about 26 minutes.

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: seven shops; the best chain 6-4-8-7, worth 25, is solid and bends at shop 8, leaving out the entrance at -6.-6283746RLRRLL

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.

The brute force on three markets and on random ones, with every step counted

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

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.

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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.