DP on Trees Diameter of Tree in DSA Java - Time & Space Complexity
We want to understand how long it takes to find the longest path in a tree using dynamic programming.
How does the time grow as the tree gets bigger?
Analyze the time complexity of the following code snippet.
// Find diameter of tree using DFS and DP
int diameter = 0;
int dfs(int node, int parent, List<List<Integer>> tree) {
int max1 = 0, max2 = 0;
for (int child : tree.get(node)) {
if (child == parent) continue;
int height = dfs(child, node, tree) + 1;
if (height > max1) {
max2 = max1;
max1 = height;
} else if (height > max2) {
max2 = height;
}
}
diameter = Math.max(diameter, max1 + max2);
return max1;
}
This code finds the diameter by exploring each node's children and tracking the two longest paths down.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive depth-first search (DFS) visiting each node once.
- How many times: Each node is visited once, and each edge is checked once in the loop.
As the number of nodes grows, the DFS visits each node and edge once, so the work grows roughly in direct proportion to the number of nodes and edges.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 node visits and 9 edge checks |
| 100 | About 100 node visits and 99 edge checks |
| 1000 | About 1000 node visits and 999 edge checks |
Pattern observation: The operations increase roughly linearly as the tree size increases.
Time Complexity: O(n)
This means the time to find the diameter grows linearly with the number of nodes in the tree.
[X] Wrong: "Since we check two longest paths at each node, the time is quadratic O(n²)."
[OK] Correct: Each node and edge is visited only once during DFS, so the total work is proportional to the number of nodes, not squared.
Understanding this linear time approach shows you can efficiently solve tree problems using DFS and dynamic programming ideas.
"What if the tree was represented as an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand the diameter definition
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 initialization
We need an array of lists, each initialized to an empty ArrayList to hold neighbors.Step 2: Check each option
List<Integer>[] tree = new ArrayList[n]; for(int i=0; i(); correctly creates an array of lists and initializes each element. Others either lack initialization or use wrong types.Final Answer:
List<Integer>[] tree = new ArrayList[n]; for(int i=0; i -> Option BQuick Check:
Array of lists with initialization [OK]
- Not initializing each list element
- Using raw List without generics
- Wrong array type declaration
int diameter = 0;
int dfs(int node, int parent, List<Integer>[] tree) {
int max1 = 0, max2 = 0;
for (int child : tree[node]) {
if (child == parent) continue;
int height = dfs(child, node, tree) + 1;
if (height > max1) {
max2 = max1;
max1 = height;
} else if (height > max2) {
max2 = height;
}
}
diameter = Math.max(diameter, max1 + max2);
return max1;
}
// call dfs(0, -1, tree) and then print diameter;Solution
Step 1: Build the tree and understand paths
The tree edges are 0-1, 1-2, 1-3. The longest path is from node 2 to node 0 or 3 to 0, length 2 edges.Step 2: Trace DFS and diameter calculation
DFS calculates max heights from children and updates diameter as sum of two largest heights. Here, diameter becomes 2.Final Answer:
2 -> Option AQuick Check:
Longest path length = 2 edges [OK]
- Counting nodes instead of edges
- Ignoring parent check in DFS
- Miscomputing max heights
int diameter = 0;
int dfs(int node, int parent, List<Integer>[] tree) {
int max1 = 0, max2 = 0;
for (int child : tree[node]) {
int height = dfs(child, node, tree) + 1;
if (height > max1) {
max2 = max1;
max1 = height;
} else if (height > max2) {
max2 = height;
}
}
diameter = Math.max(diameter, max1 + max2);
return max1;
}
// call dfs(0, -1, tree);Solution
Step 1: Analyze DFS loop over children
The code calls dfs on all children but does not skip the parent node, causing infinite recursion or stack overflow.Step 2: Identify missing condition
There should be a condition to continue if child == parent to avoid revisiting the parent.Final Answer:
Missing check to skip parent node in DFS -> Option AQuick Check:
Skip parent to prevent infinite recursion [OK]
- Forgetting to skip parent node
- Misplacing diameter update
- Returning wrong height
Solution
Step 1: Understand weighted tree diameter
In weighted trees, path length is sum of edge weights, not just number of edges.Step 2: Modify DFS height calculation
Instead of adding 1 for each edge, add the actual edge weight to the height returned by DFS.Step 3: Update diameter similarly
Diameter is max sum of two largest weighted heights from children.Final Answer:
Add edge weights to the height calculation instead of just adding 1 -> Option CQuick Check:
Use weights in height sums for weighted diameter [OK]
- Ignoring weights in diameter calculation
- Using BFS without weights
- Summing all edges instead of longest path
Ready to prove it?
Pass the test and this pattern moves unknown
