DP on Trees Diameter of Tree in DSA Javascript - Time & Space Complexity
We want to understand how long it takes to find the diameter of a tree using dynamic programming.
Specifically, how the time grows as the tree gets bigger.
Analyze the time complexity of this tree diameter code.
function treeDiameter(adj) {
let diameter = 0;
function dfs(node, parent) {
let max1 = 0, max2 = 0;
for (const child of adj[node]) {
if (child === parent) continue;
const depth = dfs(child, node) + 1;
if (depth > max1) {
max2 = max1;
max1 = depth;
} else if (depth > max2) {
max2 = depth;
}
}
diameter = Math.max(diameter, max1 + max2);
return max1;
}
dfs(0, -1);
return diameter;
}
This code finds the longest path between any two nodes in a tree using depth-first search.
Look at what repeats as the tree grows.
- Primary operation: The depth-first search (dfs) visits each node once.
- How many times: Each node is visited exactly one time, and each edge is checked once in each direction.
As the number of nodes grows, the number of steps grows roughly the same.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 visits plus edge checks |
| 100 | About 100 visits plus edge checks |
| 1000 | About 1000 visits plus edge checks |
Pattern observation: The work grows linearly with the number of nodes.
Time Complexity: O(n)
This means the time to find the diameter grows in direct proportion to the number of nodes in the tree.
[X] Wrong: "The diameter calculation takes quadratic time because it checks pairs of nodes."
[OK] Correct: The code uses a single depth-first search that visits each node once, so it does not check all pairs separately.
Understanding this linear time approach shows you can handle tree problems efficiently, a key skill in many coding challenges.
"What if we ran dfs from every node to find the diameter? How would the time complexity change?"
Practice
Solution
Step 1: Understand the definition of diameter
The diameter is the longest path between any two nodes in a tree, not just from the root.Step 2: Differentiate from other tree properties
Height is the longest path from root to leaf, number of nodes and leaves are counts, not paths.Final Answer:
The longest path between any two nodes in the tree -> Option DQuick Check:
Diameter = Longest path [OK]
- Confusing diameter with height
- Thinking diameter counts nodes instead of path length
- Assuming diameter is from root only
Solution
Step 1: Understand adjacency list structure
An adjacency list maps each node to an array of its connected nodes.Step 2: Check each option
const tree = {0: [1, 2], 1: [0], 2: [0]}; uses an object with arrays for neighbors, correct for adjacency list. const tree = [[1, 2], [], []]; misses reverse edges from 1 and 2 to 0. const tree = {0: 1, 1: 2, 2: 0}; maps nodes to single nodes, not arrays. const tree = [0, 1, 2]; is just a list of nodes, no edges.Final Answer:
const tree = {0: [1, 2], 1: [0], 2: [0]}; -> Option AQuick Check:
Adjacency list = Object with arrays [OK]
- Using single values instead of arrays for neighbors
- Confusing arrays of nodes with adjacency lists
- Not including reverse edges in undirected tree
const tree = {0: [1], 1: [0, 2, 3], 2: [1], 3: [1, 4], 4: [3]};
let maxDiameter = 0;
function dfs(node, parent) {
let max1 = 0, max2 = 0;
for (const child of tree[node]) {
if (child === parent) continue;
const length = dfs(child, node) + 1;
if (length > max1) {
max2 = max1;
max1 = length;
} else if (length > max2) {
max2 = length;
}
}
maxDiameter = Math.max(maxDiameter, max1 + max2);
return max1;
}
dfs(0, -1);
console.log(maxDiameter);Solution
Step 1: Trace DFS to find longest paths
Starting at node 0, DFS explores children. The longest path is from node 2 to node 4 passing through 1 and 3, length 3 edges.Step 2: Calculate diameter from max1 + max2
At node 1, max1=2 (to node 4), max2=1 (to node 2), sum=3 edges. At node 3, max1=1 (to node 4), max2=0, sum=1. The maximum sum found is 3 edges.Final Answer:
3 -> Option AQuick Check:
Longest path length = 3 edges [OK]
- Counting nodes instead of edges for diameter
- Ignoring the second longest path in calculation
- Not skipping parent node in DFS
const tree = {0: [1], 1: [0, 2], 2: [1]};
let maxDiameter = 0;
function dfs(node) {
let max1 = 0, max2 = 0;
for (const child of tree[node]) {
const length = dfs(child) + 1;
if (length > max1) {
max2 = max1;
max1 = length;
} else if (length > max2) {
max2 = length;
}
}
maxDiameter = Math.max(maxDiameter, max1 + max2);
return max1;
}
dfs(0);
console.log(maxDiameter);Solution
Step 1: Identify missing parent check in DFS
The DFS function does not skip the parent node, causing it to revisit the previous node endlessly, leading to infinite recursion.Step 2: Understand impact of missing parent check
Without skipping the parent, the DFS calls loop back and forth between nodes, never terminating.Final Answer:
Missing parent check causes infinite recursion -> Option CQuick Check:
Always skip parent node in DFS to avoid loops [OK]
- Not passing parent node in DFS
- Assuming tree edges are directed
- Updating maxDiameter incorrectly
{0: [[1, 3], [2, 5]], 1: [[0, 3], [3, 4]], 2: [[0, 5]], 3: [[1, 4]]}. How would you modify the diameter calculation to find the longest weighted path?Solution
Step 1: Incorporate edge weights in DFS
Modify DFS to add the weight of each edge when calculating path lengths instead of just adding 1.Step 2: Update maxDiameter with weighted sums
At each node, update maxDiameter with the sum of the two longest weighted paths found among children.Final Answer:
Add edge weights to the DFS return values and update maxDiameter accordingly -> Option BQuick Check:
Weighted diameter needs weighted path sums [OK]
- Ignoring weights and counting edges only
- Using BFS without weights for diameter
- Summing all weights instead of longest path
Ready to prove it?
Pass the test and this pattern moves unknown
