THN Interview Prep

437. Path Sum III

At a Glance

  • Topic: trees
  • Pattern: Prefix sum on tree + Tree DFS
  • Difficulty: Medium
  • Companies: Amazon, Google, Microsoft, Meta, Apple
  • Frequency: High
  • LeetCode: 437

Problem (one-liner)

Given the root of a binary tree and an integer targetSum, return the number of downward paths (node→…→descendant, length ≥ 1) whose values sum to targetSum. Paths do not need to start at the root or end at a leaf.

Recognition Cues

  • Count paths with sum constraint on trees (vertical chains)
  • Same “prefix sum ↔ complement” idea as arrays, but prefix is root-to-current on current DFS branch
  • Must reset frequency map when unwinding (different branches share prefixes differently)

Diagram

At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.

Loading diagram…

Approaches

  • Brute force — try every ancestor as start, DFS down — O(n²) on skew trees.
  • Better — prefix hashmap per root-to-node path — O(n) time average if map ops O(1).
  • OptimalO(n) time / O(h) stack + map — DFS from root; map counts prefix sums; on exit decrement count for current prefix.

Optimal Solution

Go

package main

type TreeNode struct {
	Val   int
	Left  *TreeNode
	Right *TreeNode
}

func pathSum(root *TreeNode, targetSum int) int {
	counts := map[int]int{0: 1}
	var walk func(*TreeNode, int) int
	walk = func(node *TreeNode, prefix int) int {
		if node == nil {
			return 0
		}
		prefix += node.Val
		ways := counts[prefix-targetSum]
		counts[prefix]++
		ways += walk(node.Left, prefix) + walk(node.Right, prefix)
		counts[prefix]--
		return ways
	}
	return walk(root, 0)
}

JavaScript

class TreeNode {
	constructor(val = 0, left = null, right = null) {
		this.val = val;
		this.left = left;
		this.right = right;
	}
}

function pathSum(root, targetSum) {
	const counts = new Map([[0, 1]]);
	function walk(node, prefix) {
		if (node === null) {
			return 0;
		}
		prefix += node.val;
		let ways = counts.get(prefix - targetSum);
		if (ways === undefined) {
			ways = 0;
		}
		counts.set(prefix, (counts.get(prefix) ?? 0) + 1);
		ways += walk(node.left, prefix) + walk(node.right, prefix);
		counts.set(prefix, counts.get(prefix) - 1);
		return ways;
	}
	return walk(root, 0);
}

Walkthrough

Let targetSum = 8. Suppose along one downward walk prefixes hit {0:1, 5:1, 8:1} when prefix becomes 8: counts[8-8]=counts[0]=1 records one path ending at the current node. If a child later also reaches prefix=8, you may count another path that started below the original root of the subcall—map updates on the recursion stack keep branch-local history.

eventprefixadd to answermap note
start0 (virtual){0:1}
visit node 55counts[5-8]increment 5
return / unwinddecrement 5 so parallel branches stay independent

Invariant: counts only tracks prefix frequencies on the active path from the global root; post-order cleanup keeps cousin subtrees from sharing prefix state incorrectly.

Edge Cases

  • Negative values — cannot prune by monotonicity; map approach still correct
  • Zero target — multiple overlaps possible; map counts handle multiplicity
  • targetSum equals single node value — path length 1

Pitfalls

  • Forgetting counts[0]=1 baseline (empty prefix before root)
  • Not undoing counts[prefix]++ after processing node (breaks sibling paths)

Similar Problems

Variants

  • Path must start at root or end at leaf only — tighter constraints, often simpler DFS.
  • Path Sum I style existence check — boolean DFS without counting multiplicity.

Mind-Map Tags

#trees #prefix-sum #hashmap #backtrack-unwind #path-count

Last updated on

Spotted something unclear or wrong on this page?

On this page