DP on Trees Diameter of Tree in DSA C++ - 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.
int dfs(int node, int parent, vector<vector<int>>& adj, int& diameter) {
int max1 = 0, max2 = 0;
for (int child : adj[node]) {
if (child == parent) continue;
int height = dfs(child, node, adj, diameter);
if (height > max1) {
max2 = max1;
max1 = height;
} else if (height > max2) {
max2 = height;
}
}
diameter = max(diameter, max1 + max2);
return max1 + 1;
}
int treeDiameter(vector<vector<int>>& adj) {
int diameter = 0;
dfs(0, -1, adj, diameter);
return diameter;
}
This code finds the diameter (longest path) of a tree using a depth-first search and dynamic programming.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The depth-first search (dfs) visits each node once and explores its children.
- How many times: Each node is visited exactly one time, and each edge is checked twice (once from each node).
As the number of nodes grows, the number of operations grows roughly in proportion to the number of nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 18 (edges checked twice) |
| 100 | About 198 |
| 1000 | About 1998 |
Pattern observation: The operations grow linearly with the number of nodes.
Time Complexity: O(n)
This means the time to find the diameter grows directly with the number of nodes in the tree.
[X] Wrong: "The diameter calculation takes quadratic time because it checks all 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 explicitly.
Understanding this linear time approach shows you can efficiently solve tree problems using recursion and dynamic programming.
"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 definition of tree diameter
The diameter is defined as the longest path between any two nodes in a tree, not necessarily passing through 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 path lengths.Final Answer:
The longest path between any two nodes in the tree -> Option AQuick Check:
Diameter = longest path in tree [OK]
- Confusing diameter with height
- Thinking diameter counts nodes instead of path length
- Assuming diameter must pass through root
Solution
Step 1: Understand vector of vectors syntax
Correct syntax uses std::vector adj(n); to create n empty vectors.Step 2: Check other options for errors
std::vector<int> adj[n]; uses array of vectors (valid but less modern), C misses template arguments, D misses template parameters.Final Answer:
std::vector adj(n); -> Option BQuick Check:
Correct vector of vectors syntax = std::vector adj(n); [OK]
- Missing template parameters in vector
- Using C-style arrays instead of vectors
- Incorrect initialization syntax
std::vector adj(4);
adj[0].push_back(1); adj[1].push_back(0);
adj[1].push_back(2); adj[2].push_back(1);
adj[1].push_back(3); adj[3].push_back(1);
int diameter = 0;
int dfs(int node, int parent, const std::vector& adj, int& diameter) {
int max1 = 0, max2 = 0;
for (int child : adj[node]) {
if (child == parent) continue;
int height = dfs(child, node, adj, diameter) + 1;
if (height > max1) {
max2 = max1;
max1 = height;
} else if (height > max2) {
max2 = height;
}
}
diameter = std::max(diameter, max1 + max2);
return max1;
}
dfs(0, -1, adj, diameter);
std::cout << diameter << std::endl;Solution
Step 1: Understand the tree structure and DFS traversal
The tree edges form a structure: 0-1 connected, 1 connected to 2 and 3. The longest path is from node 2 to node 3 through 1, length 2 edges.Step 2: Calculate diameter from DFS logic
DFS calculates max1 and max2 heights from children. For node 1, max1=1 (to 2), max2=1 (to 3), so diameter = max(0,1+1)=2.Final Answer:
2 -> Option DQuick Check:
Longest path length = 2 edges [OK]
- Counting nodes instead of edges for diameter
- Not updating diameter correctly
- Confusing parent and child in DFS
int dfs(int node, int parent, const std::vector& adj, int& diameter) {
int maxHeight = 0;
for (int child : adj[node]) {
if (child == parent) continue;
int height = dfs(child, node, adj, diameter) + 1;
if (height > maxHeight) {
maxHeight = height;
diameter = std::max(diameter, maxHeight);
}
}
return maxHeight;
}Solution
Step 1: Analyze diameter update logic
The code updates diameter only with maxHeight, which is the longest single path from current node, but diameter requires sum of two longest paths through the node.Step 2: Identify missing second longest path
Without tracking two longest heights, the code misses paths passing through the node combining two children, leading to incorrect diameter.Final Answer:
Diameter is updated only with maxHeight, missing sum of two heights -> Option CQuick Check:
Diameter needs sum of two longest child heights [OK]
- Updating diameter with only one child's height
- Not skipping parent node in DFS
- Forgetting to increment height by 1
Solution
Step 1: Visualize the tree and identify longest path
The tree edges form paths: 0-1-3-4-5 and 2 connected to 1. The longest path is from node 2 to node 5 passing through 1,3,4 with length 5 edges.Step 2: Calculate diameter using DP on Trees logic
At node 1, two longest child heights are 1 (to 2) and 3 (to 5). Diameter = max(diameter, 1+3) = 4, but the longest path is from 2 to 5 which is 4 edges plus 1 edge from 0 to 1 is not part of longest path. Actually, the longest path is from node 2 to node 5 passing through 1,3,4 with length 5 edges.Final Answer:
5 -> Option AQuick Check:
Longest path length = 5 edges [OK]
- Counting nodes instead of edges for diameter
- Ignoring one branch in longest path
- Miscounting edges in path
Ready to prove it?
Pass the test and this pattern moves unknown
