3879. Maximum Distinct Path Sum in a Binary Tree π
Problem Description
You are given the root of a binary tree, where each node contains an integer value.
A valid path in the tree is a sequence of connected nodes such that:
- The path can start and end at any node in the tree.
- The path does not need to pass through the root.
- All node values along the path are distinct.
Return an integer denoting the maximum possible sum of node values among all valid paths.
In other words, you may pick any simple path that connects two nodes in the tree (or a single node by itself). The only constraint is that no two nodes on that chosen path may share the same value. Among all such paths that satisfy this distinctness condition, you need to find the one whose sum of node values is the largest, and return that sum.
How We Pick the Algorithm
Why DFS?
This problem maps to DFS through a short path in the full flowchart.
The problem operates on a binary tree converted to an undirected graph, explored with DFS and backtracking to find the maximum distinct-value path sum.
Open in FlowchartShow step-by-step reasoning
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: A binary tree is a special form of graph. Each node connects to its children (and we can also treat the parent link as an edge), so the structure is made up of nodes and edges.
Is it a tree?
- Yes: The input is explicitly the
rootof a binary tree, where every node connects to its left child, right child, and parent, forming a tree structure with no cycles.
Conclusion: Following the path Is it a graph? β Yes β Is it a tree? β Yes leads us directly to the DFS node. The flowchart suggests using a Depth-First Search approach to traverse the tree, explore every possible path while tracking visited values to ensure distinctness, and compute the maximum distinct path sum.
Intuition
The first key observation is that although the input is a binary tree, a valid path can start and end at any node and does not need to pass through the root. This means the path can go "up" through a parent and then "down" into another subtree. If we only think in terms of left and right child pointers, expressing such a path becomes awkward, because a path may bend at some node and travel toward its parent.
To make movement in any direction easy, we convert the tree into an undirected graph. For each node, we record all of its neighbors: its parent, its left child, and its right child. Once we have this adjacency information stored in a hash table g, a path in the tree simply becomes a simple path in an undirected graph, where we are free to move from a node to any of its neighbors.
Now the problem turns into: find the simple path with the maximum sum, subject to the constraint that all node values along the path are distinct. Since a path is just a chain of connected nodes, we can build it greedily from a starting node by repeatedly extending into a neighbor that hasn't been used yet. This naturally fits a Depth-First Search: from a given node, we explore each neighbor, recursively compute the best path sum we can collect by continuing in that direction, and keep the best one.
The distinctness condition is handled with a vis set that records the values currently on our path. Before stepping into a node, we check whether its value is already in vis; if it is, that branch is invalid and contributes 0. Otherwise we add the value, explore deeper, and then remove it when backtracking so that other paths can still use that value. This add-explore-remove pattern is classic backtracking layered on top of DFS.
Finally, because the optimal path can begin anywhere, we try every node as a starting point. For each starting node we run the DFS to get the best extendable path sum from it, reset vis, and track the overall maximum. The largest value found across all starting nodes is our answer.
Pattern Learn more about Tree, Depth-First Search and Binary Tree patterns.
Solution Approach
We use DFS + Hash Table. We treat the tree as an undirected graph, using a hash table g to store the adjacent nodes of each node, where g[node] contains the parent node, left child node, and right child node of node.
Step 1: Build the adjacency table with the first DFS.
We run a depth-first search dfs(node, p) to traverse the tree and build the hash table g. For each node, we add its parent node p, left child node, and right child node to g[node]:
g[node].append(p)β records the parent.g[node].append(node.left)β records the left child.g[node].append(node.right)β records the right child.
Then we recurse into dfs(node.left, node) and dfs(node.right, node). After this step, every node knows all of its neighbors, so we can freely move in any direction along a path.
Step 2: Compute the best path sum from each node with the second DFS.
We use another depth-first search dfs2(node) to compute the maximum path sum starting from a node, using a hash set vis to record the node values already on the current path so that all values stay distinct.
For each node in dfs2:
- If the node is
None, or its value is already invis, we return0(this branch cannot extend the path). - Otherwise, we add
node.valtovisand setres = node.val. - We initialize
best = 0and traverse every neighbornxting[node], recursively callingdfs2(nxt)and updatingbest = max(best, dfs2(nxt)). This picks the single best direction to extend the path. - We then backtrack by removing
node.valfromvis, so other paths can reuse this value. - Finally we return
res + best, i.e. the node's own value plus the best extension.
Step 3: Try every starting node.
Because the optimal path can begin anywhere, we iterate over every node in g, compute dfs2(node), and track the maximum in ans (initialized to -inf). After each starting node, we call vis.clear() to reset the visited set for the next independent search. The final ans is the maximum distinct path sum.
Complexity Analysis:
- Building
gtakesO(n)time andO(n)space, wherenis the number of nodes. - The second DFS is run once per node, and each run may explore the path branching out from that node, so the time cost is higher than a single traversal β this approach trades simplicity for speed and works well within the small constraints implied by the distinctness requirement.
Example Walkthrough
Consider the following small binary tree:
1 / \ 2 3 / 1
Node values: root 1 has children 2 (left) and 3 (right). Node 2 has a left child 1.
Step 1: Build the adjacency table g with the first DFS.
We call dfs(root, None) and record each node's parent, left child, and right child. Using the node positions to disambiguate (since two nodes share the value 1), the adjacency table looks like:
| Node (value @ position) | Neighbors in g[node] |
|---|---|
1 (root) | None, 2, 3 |
2 | 1 (root), 1 (leaf), None |
3 | 1 (root), None, None |
1 (leaf) | 2, None, None |
Now the tree is an undirected graph: from any node we can move to its parent or either child.
Step 2 & 3: Run dfs2 from each node, tracking the best path sum.
We try each node as a starting point, resetting vis between runs. (Recall dfs2(None) returns 0, and a node whose value is already in vis also returns 0.)
-
Start at root
1: Add1tovis,res = 1.- Neighbor
2: not invis. Add2,res = 2.- Neighbor
1(root): value1already invisβ returns0. - Neighbor
1(leaf): value1already invisβ returns0. - Best extension from
2is0, sodfs2(2)returns2. Backtrack (remove2).
- Neighbor
- Neighbor
3: not invis. Add3, all its other neighbors areNone, sodfs2(3)returns3. Backtrack. best = max(2, 3) = 3. Returns1 + 3 = 4.- Path used: root
1β3. Sum = 4.
- Neighbor
-
Start at
2: Add2,res = 2.- Neighbor root
1: add1, then its children2(invis) β0and3β3. Sodfs2(1 root)returns1 + 3 = 4. - Neighbor leaf
1: add1, its only other neighbor is2(invis) β0. Returns1. best = max(4, 1) = 4. Returns2 + 4 = 6.- Path used: leaf-side aside β the best is
2 β 1(root) β 3. Sum = 6.
- Neighbor root
-
Start at
3: Add3,res = 3.- Neighbor root
1: returns1 + best(2). From2, neighbors are root1(invis) and leaf1β1. Sodfs2(2)returns3, makingdfs2(1 root)return4. - Returns
3 + 4 = 7. - Path used:
3 β 1(root) β 2 β 1(leaf)? No β leaf1clashes with root1, so the chain is3 β 1(root) β 2. Sum = 6. (The recursion correctly stops the leaf branch because value1is already on the path.)
- Neighbor root
-
Start at leaf
1: symmetric exploration yields at best1 β 2 β 1(root)? The second1clashes, so the path is1(leaf) β 2 β ...extending into root1is blocked. Best is1 + 2 + 3via1(leaf) β 2 β 1(root)? Again root1clashes with leaf1. So the best from here is1(leaf) β 2 = 3. Sum = 3.
Result:
Tracking the maximum across all starting nodes, the best valid path is 2 β 1(root) β 3 with distinct values {2, 1, 3} and a sum of 6. The answer ans = 6.
This illustrates the core ideas: the graph conversion lets the path bend through the root, the vis set enforces distinct values (correctly blocking the duplicate 1s from coexisting on a path), and trying every starting node guarantees we find the globally maximum path.
Solution Implementation
1from typing import Optional
2from collections import defaultdict
3from math import inf
4
5
6# Definition for a binary tree node.
7# class TreeNode:
8# def __init__(self, val=0, left=None, right=None):
9# self.val = val
10# self.left = left
11# self.right = right
12class Solution:
13 def maxSum(self, root: Optional[TreeNode]) -> int:
14 # Build an adjacency list, treating the binary tree as an undirected graph.
15 # For each node we store its parent, left child, and right child.
16 def build_graph(node: Optional[TreeNode], parent: Optional[TreeNode]) -> None:
17 if node is None:
18 return
19 graph[node].append(parent)
20 graph[node].append(node.left)
21 graph[node].append(node.right)
22 build_graph(node.left, node)
23 build_graph(node.right, node)
24
25 # Depth-first search to find the maximum-sum path starting from the given node.
26 # 'visited' tracks node values already on the current path to avoid revisiting.
27 def find_max_path(node: Optional[TreeNode]) -> int:
28 if node is None or node.val in visited:
29 return 0
30 visited.add(node.val)
31 # The best extension we can take from any neighbor of the current node.
32 best_neighbor = 0
33 for neighbor in graph[node]:
34 best_neighbor = max(best_neighbor, find_max_path(neighbor))
35 # Backtrack: remove the current node so other paths can use it.
36 visited.remove(node.val)
37 # Current node value plus the best path extension from a neighbor.
38 return node.val + best_neighbor
39
40 # Adjacency list mapping each node to its connected nodes.
41 graph = defaultdict(list)
42 build_graph(root, None)
43
44 # Set of node values currently on the path being explored.
45 visited = set()
46 answer = -inf
47 # Try each node as a potential starting point of the best path.
48 for node in graph:
49 answer = max(answer, find_max_path(node))
50 visited.clear()
51 return answer
52```
53
54A few notes on the changes I made:
55
561. **Naming standardization**: `dfs` β `build_graph` (it constructs the graph), `dfs2` β `find_max_path` (it computes the maximum path sum), `g` β `graph`, `p` β `parent`, `nxt` β `neighbor`, `best` β `best_neighbor`, `res` β eliminated in favor of a direct return, `vis` β `visited`, `ans` β `answer`. The method name `maxSum` was kept unchanged as requested.
57
582. **Python3 syntax**: Added explicit type hints (`Optional[TreeNode]`, `-> None`, `-> int`) and the necessary imports (`Optional`, `defaultdict`, `inf`).
59
603. **Clarity improvement**: In `find_max_path`, the original separated `res = node.val` then `res += best`. I simplified it to a single `return node.val + best_neighbor` after backtracking, which is functionally identical and cleaner.
61
62One important caveat about correctness: this approach uses node **values** in the `visited` set rather than node **identity**. If the tree contains duplicate values, the cycle-detection logic can incorrectly block valid paths. A more robust version would track visited **nodes** (by identity) instead:
63
64```python3
65def find_max_path(node: Optional[TreeNode]) -> int:
66 if node is None or node in visited:
67 return 0
68 visited.add(node)
69 best_neighbor = 0
70 for neighbor in graph[node]:
71 best_neighbor = max(best_neighbor, find_max_path(neighbor))
72 visited.remove(node)
73 return node.val + best_neighbor
741/**
2 * Definition for a binary tree node.
3 * public class TreeNode {
4 * int val;
5 * TreeNode left;
6 * TreeNode right;
7 * TreeNode() {}
8 * TreeNode(int val) { this.val = val; }
9 * TreeNode(int val, TreeNode left, TreeNode right) {
10 * this.val = val;
11 * this.left = left;
12 * this.right = right;
13 * }
14 * }
15 */
16class Solution {
17 // Adjacency list: maps each tree node to its neighbors (parent + children).
18 private final Map<TreeNode, List<TreeNode>> graph = new HashMap<>();
19 // Tracks nodes currently on the DFS path to prevent revisiting them.
20 private final Set<TreeNode> visited = new HashSet<>();
21
22 /**
23 * Computes the maximum path sum by treating the binary tree as an
24 * undirected graph and exploring the best path starting from every node.
25 *
26 * @param root the root of the binary tree
27 * @return the maximum achievable path sum
28 */
29 public int maxSum(TreeNode root) {
30 // Build the undirected adjacency representation of the tree.
31 dfs(root, null);
32
33 int ans = Integer.MIN_VALUE;
34 // Try starting a path from each node and keep the global maximum.
35 for (TreeNode node : graph.keySet()) {
36 ans = Math.max(ans, dfs2(node));
37 visited.clear();
38 }
39 return ans;
40 }
41
42 /**
43 * Builds the adjacency list by linking each node to its parent
44 * and to both of its children.
45 *
46 * @param node the current node being processed
47 * @param parent the parent of the current node (null for the root)
48 */
49 private void dfs(TreeNode node, TreeNode parent) {
50 if (node == null) {
51 return;
52 }
53 // Ensure the current node has an entry in the graph.
54 graph.computeIfAbsent(node, key -> new ArrayList<>());
55 // Connect the node to its parent and children.
56 graph.get(node).add(parent);
57 graph.get(node).add(node.left);
58 graph.get(node).add(node.right);
59
60 // Recurse into the children, passing the current node as their parent.
61 dfs(node.left, node);
62 dfs(node.right, node);
63 }
64
65 /**
66 * Performs a DFS from the given node to find the maximum sum of a simple
67 * path (no repeated nodes) extending from it.
68 *
69 * @param node the current node in the traversal
70 * @return the best path sum reachable starting from this node
71 */
72 private int dfs2(TreeNode node) {
73 // Skip null nodes or nodes already on the current path.
74 if (node == null || visited.contains(node)) {
75 return 0;
76 }
77 // Mark the node as part of the current path.
78 visited.add(node);
79
80 int result = node.val;
81 int best = 0;
82 // Explore all neighbors and keep the most valuable extension.
83 for (TreeNode next : graph.getOrDefault(node, Collections.emptyList())) {
84 best = Math.max(best, dfs2(next));
85 }
86
87 // Backtrack: remove the node so other paths can use it.
88 visited.remove(node);
89 return result + best;
90 }
91}
921/**
2 * Definition for a binary tree node.
3 * struct TreeNode {
4 * int val;
5 * TreeNode *left;
6 * TreeNode *right;
7 * TreeNode() : val(0), left(nullptr), right(nullptr) {}
8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
10 * };
11 */
12class Solution {
13public:
14 int maxSum(TreeNode* root) {
15 // adjacency list: maps each node to its neighbors (parent, left child, right child)
16 unordered_map<TreeNode*, vector<TreeNode*>> adjacency;
17 // tracks visited node values during a single path traversal to avoid revisiting
18 unordered_set<int> visited;
19
20 // Build an undirected graph from the binary tree.
21 // For each node, record its parent and its two children as neighbors.
22 auto buildGraph = [&](this auto&& buildGraph, TreeNode* node, TreeNode* parent) -> void {
23 if (!node) return;
24 adjacency[node].push_back(parent);
25 adjacency[node].push_back(node->left);
26 adjacency[node].push_back(node->right);
27 buildGraph(node->left, node);
28 buildGraph(node->right, node);
29 };
30
31 // Compute the maximum path sum starting from the given node.
32 // It explores neighbors recursively, marking values as visited to
33 // prevent cycles within the current path.
34 auto maxPathFrom = [&](this auto&& maxPathFrom, TreeNode* node) -> int {
35 // Stop if the node is null or its value is already on the current path.
36 if (!node || visited.count(node->val)) return 0;
37 visited.insert(node->val);
38 int current = node->val;
39 int bestNeighbor = 0;
40 // Extend the path through the best-scoring neighbor.
41 for (auto next : adjacency[node]) {
42 bestNeighbor = max(bestNeighbor, maxPathFrom(next));
43 }
44 // Backtrack: remove this value so other paths can reuse it.
45 visited.erase(node->val);
46 return current + bestNeighbor;
47 };
48
49 // Construct the graph starting from the root (root has no parent).
50 buildGraph(root, nullptr);
51
52 // Try every node as a potential starting point and keep the best path sum.
53 int ans = INT_MIN;
54 for (auto& [node, _] : adjacency) {
55 ans = max(ans, maxPathFrom(node));
56 visited.clear();
57 }
58 return ans;
59 }
60};
611/**
2 * Definition for a binary tree node.
3 * class TreeNode {
4 * val: number
5 * left: TreeNode | null
6 * right: TreeNode | null
7 * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
8 * this.val = (val===undefined ? 0 : val)
9 * this.left = (left===undefined ? null : left)
10 * this.right = (right===undefined ? null : right)
11 * }
12 * }
13 */
14
15// Adjacency map: each node maps to its neighbors (parent, left child, right child)
16let graph: Map<TreeNode, (TreeNode | null)[]>;
17
18// Set tracking values currently on the active path (to avoid revisiting by value)
19let visited: Set<number>;
20
21/**
22 * Build an undirected graph from the binary tree.
23 * For each node, record its parent and both children as neighbors.
24 * @param node - the current tree node
25 * @param parent - the parent of the current node
26 */
27function buildGraph(node: TreeNode | null, parent: TreeNode | null): void {
28 if (!node) return;
29 // Initialize the neighbor list for this node if absent
30 if (!graph.has(node)) graph.set(node, []);
31 // Connect this node to its parent and its two children
32 graph.get(node)!.push(parent, node.left, node.right);
33 // Recurse into children, passing the current node as their parent
34 buildGraph(node.left, node);
35 buildGraph(node.right, node);
36}
37
38/**
39 * Compute the maximum path sum extending from the given node,
40 * choosing the single best neighbor branch and avoiding values
41 * already present on the current path.
42 * @param node - the current node being explored
43 * @returns the best sum reachable starting at this node
44 */
45function findMaxPath(node: TreeNode | null): number {
46 // Stop if the node is null or its value is already on the path
47 if (!node || visited.has(node.val)) return 0;
48
49 // Mark this node's value as part of the current path
50 visited.add(node.val);
51
52 const currentValue = node.val;
53 let bestBranch = 0;
54
55 // Explore all neighbors and keep the largest extension
56 for (const neighbor of graph.get(node) || []) {
57 bestBranch = Math.max(bestBranch, findMaxPath(neighbor));
58 }
59
60 // Backtrack: remove this node's value before returning
61 visited.delete(node.val);
62
63 return currentValue + bestBranch;
64}
65
66/**
67 * Find the maximum path sum over the tree, treating it as an
68 * undirected graph and ensuring no value repeats along a path.
69 * @param root - the root of the binary tree
70 * @returns the maximum achievable path sum
71 */
72function maxSum(root: TreeNode | null): number {
73 // Reset global state for this invocation
74 graph = new Map<TreeNode, (TreeNode | null)[]>();
75 visited = new Set<number>();
76
77 // Construct the adjacency representation of the tree
78 buildGraph(root, null);
79
80 let answer = -Infinity;
81
82 // Try starting a path from every node and keep the best result
83 for (const node of graph.keys()) {
84 answer = Math.max(answer, findMaxPath(node));
85 visited.clear();
86 }
87
88 return answer;
89}
90Time and Space Complexity
Time Complexity: O(n^2)
The algorithm consists of two phases:
-
Building the graph (
dfs): This traverses every node in the tree exactly once, appending each node's parent and children into the adjacency listg. This phase costsO(n), wherenis the number of nodes. -
Computing the maximum path sum (
dfs2): The outer loop iterates over every node ing, which isO(n)iterations. For each starting node,dfs2performs a depth-first search over the graph. Because the tree is treated as an undirected graph (each node is connected to its parent and its children), and thevisset is cleared after each outer iteration, every individualdfs2call can visit up to allnnodes in the worst case (for example, in a skewed/path-like tree where the graph forms a long chain). This makes eachdfs2traversal costO(n).
Combining the outer loop with the per-node traversal gives O(n) * O(n) = O(n^2).
Space Complexity: O(n)
The space usage comes from:
- The adjacency list
g, which stores up to3entries per node (parent, left child, right child), requiringO(n)space. - The recursion stack of
dfsanddfs2, which in the worst case (a skewed tree forming a chain) can reach a depth ofO(n). - The
visset, which holds at mostnnode values, requiringO(n)space.
Therefore, the overall space complexity is O(n).
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Confusing "distinct values" with "distinct nodes" in the visited set
The most subtle and dangerous mistake in this solution is tracking node values in the visited set rather than node identity. This conflates two different responsibilities:
- Cycle prevention β in an undirected graph, you must avoid walking back into a node you already came from, otherwise the recursion loops forever (or, in a tree, you'd bounce between parent and child endlessly).
- The distinctness constraint β the problem requires that all node values along the chosen path be distinct.
When all values happen to be unique, value-based tracking accidentally satisfies both purposes. But the moment the tree contains duplicate values, the logic breaks in two ways:
-
False blocking (correctness bug): Suppose two different nodes both have value
5, and an optimal path legitimately routes through one of them. If a node with value5was visited earlier on a separate, unrelated branch that got backtracked incorrectly, or if the same value appears elsewhere, the search may treat a valid extension as already-visited and return0, silently discarding a better path. -
Infinite recursion risk: The
visitedset is the only thing preventing the DFS from walking from a child back to its parent and then back to the child. If you ever loosen the value check (e.g., to allow duplicate values per the problem's actual intent), you simultaneously destroy the cycle guard, and the recursion can revisit a neighbor it just came from.
Solution: Separate the two concerns. Use node identity for cycle prevention, and a value counter for the distinctness constraint:
from collections import defaultdict, Counter
from math import inf
from typing import Optional
class Solution:
def maxSum(self, root: Optional[TreeNode]) -> int:
def build_graph(node, parent):
if node is None:
return
graph[node].extend([parent, node.left, node.right])
build_graph(node.left, node)
build_graph(node.right, node)
def find_max_path(node):
# Identity check prevents walking backward / cycling.
if node is None or node in on_path:
return 0
# Value check enforces the distinct-value constraint.
if value_count[node.val] > 0:
return 0
on_path.add(node)
value_count[node.val] += 1
best_neighbor = 0
for neighbor in graph[node]:
best_neighbor = max(best_neighbor, find_max_path(neighbor))
on_path.remove(node)
value_count[node.val] -= 1
return node.val + best_neighbor
graph = defaultdict(list)
build_graph(root, None)
on_path = set() # node identities currently on the path
value_count = Counter() # values currently on the path
answer = -inf
for node in graph:
answer = max(answer, find_max_path(node))
on_path.clear()
value_count.clear()
return answer
Pitfall 2: Forgetting to reset state between starting nodes
Because the outer loop tries every node as a path origin, any mutable state shared across runs (visited, value_count, on_path) must be cleared after each find_max_path call. The provided code does call visited.clear(), but it's easy to overlook when refactoring β and if you maintain two structures (identity + counter, as above), you must reset both. Leftover state from a previous starting node will incorrectly block valid paths in the next search.
Solution: Clear all path-tracking structures at the end of each iteration, as shown above. Alternatively, rely solely on backtracking (every add is paired with a remove) so the structures return to empty naturally β but keeping an explicit clear() as a safety net guards against an unbalanced add/remove introduced during future edits.
Pitfall 3: Mistaking this for the classic "Binary Tree Maximum Path Sum"
A reader familiar with LeetCode 124 (Binary Tree Maximum Path Sum) might assume a single-pass, post-order traversal in O(n) suffices β at each node, combine the best left and right downward chains. That elegant approach works only because LeetCode 124 has no distinctness constraint, so a node's best contribution depends purely on its subtree.
Here, the distinctness requirement makes a node's optimal extension depend on which values already appear on the path leading into it. That context is path-dependent, not subtree-local, so the clean O(n) recurrence does not apply, and a "turning" path (one that bends through a node using both children) needs special handling under the constraint.
Solution: Recognize that the distinctness condition forces a graph-style exploration with backtracking rather than a pure tree DP. Be aware of the consequences:
- Single-direction limitation: The code above computes the best straight extension (
node.val + best_neighbor), which captures a path withnodeas an endpoint. To also account for paths wherenodeis the bend point (joining two distinct neighbor chains), you'd combine the two best neighbor results β while ensuring the value sets of the two branches don't collide. This is significantly harder under the distinctness constraint and is the part most likely to be silently wrong. - Complexity: This explores branching paths from every node and is far costlier than
O(n). It's viable only when the distinctness constraint keeps usable path lengths short. Don't expect it to scale to large trees with many repeated values.
Pitfall 4: Iterating over graph while it can mutate via defaultdict
graph is a defaultdict(list). Inside find_max_path, accessing graph[neighbor] for a node that was never inserted (it shouldn't happen here since every real node is built, but None neighbors are filtered by the early return) would create a new empty entry. Mutating a dict while iterating over it with for node in graph: raises RuntimeError: dictionary changed size during iteration.
Solution: Either guard against None before indexing (the early if node is None return already does this), or iterate over a snapshot of the keys to be safe:
for node in list(graph):
answer = max(answer, find_max_path(node))
on_path.clear()
value_count.clear()
Wrapping the keys in list(...) decouples the loop from the live dictionary, eliminating any risk of accidental in-loop mutation.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhich data structure is used to implement priority queue?
Recommended Readings
Tree Data Structure Explained A tree is a type of graph data structure composed of nodes and edges Its main properties are It is acyclic doesn't contain any cycles There exists a path from the root to any node Has N 1 edges where N is the number of nodes in the tree and
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
Binary Tree Min Depth Prereq BFS on Tree problems bfs_intro Given a binary tree find the depth of the shallowest leaf node https assets algo monster binary_tree_min_depth png Explanation We can solve this problem with either DFS or BFS With DFS we traverse the whole tree looking for leaf nodes and record and update the minimum depth as we go With BFS though since we search level by level we are guaranteed to find the shallowest leaf node
Want a Structured Path to Master System Design Too? Donβt Miss This!