DP on Trees Diameter of Tree in DSA Python - 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.
def diameter_tree(graph, node, parent):
max1, max2 = 0, 0
for child in graph[node]:
if child == parent:
continue
length = diameter_tree(graph, child, node) + 1
if length > max1:
max2 = max1
max1 = length
elif length > max2:
max2 = length
diameter_tree.result = max(diameter_tree.result, max1 + max2)
return max1
# diameter_tree.result = 0 before calling
This code finds the diameter (longest path) of a tree using a depth-first search with dynamic programming.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls visiting each node once.
- How many times: Each node is visited exactly one time.
As the number of nodes grows, the function visits each node once and does a small fixed amount of work per node.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 visits |
| 100 | About 100 visits |
| 1000 | About 1000 visits |
Pattern observation: The work grows linearly with the number of nodes.
Time Complexity: O(n)
This means the time to find the diameter grows directly in proportion to 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 recursion and dynamic programming to visit each node once, avoiding repeated work on pairs.
Understanding this linear time approach shows you can handle tree problems efficiently, a key skill in many coding challenges.
"What if the tree was represented as a list of edges instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand the definition of tree diameter
The diameter is the longest path between any two nodes, not just from the root or leaf count.Step 2: Compare options with definition
Only The longest path between any two nodes in the tree correctly describes the longest path between any two nodes.Final Answer:
The longest path between any two nodes in the tree -> Option CQuick Check:
Diameter = longest path [OK]
- Confusing diameter with tree height
- Thinking diameter is number of leaves
- Assuming diameter is total nodes count
Solution
Step 1: Identify correct Python function syntax
Python functions use 'def' keyword followed by function name and parentheses with parameters.Step 2: Check each option
def diameter(node): uses correct Python syntax. function diameter(node) { is JavaScript style. def diameter[node]: uses wrong brackets. diameter = lambda node: is incomplete lambda syntax.Final Answer:
def diameter(node): -> Option AQuick Check:
Python function syntax = def name(params): [OK]
- Using curly braces instead of colon
- Using square brackets for parameters
- Incorrect lambda syntax for recursion
edges = [(1,2), (2,3), (2,4), (4,5)]
# Code snippet
from collections import defaultdict
tree = defaultdict(list)
for u,v in edges:
tree[u].append(v)
tree[v].append(u)
max_diameter = 0
def dfs(node, parent):
global max_diameter
max1 = max2 = 0
for child in tree[node]:
if child == parent:
continue
depth = dfs(child, node)
if depth > max1:
max2 = max1
max1 = depth
elif depth > max2:
max2 = depth
max_diameter = max(max_diameter, max1 + max2)
return max1 + 1
dfs(1, -1)
print(max_diameter)Solution
Step 1: Build the tree and understand structure
The tree edges form: 1-2-3, 2-4-5. The longest path is from node 3 to node 5 via 2 and 4.Step 2: Calculate longest path length
Path 3->2->4->5 has 3 edges, so diameter is 3 edges = 4 nodes in path length. The code counts edges, so max_diameter = 3 edges.Final Answer:
3 -> Option AQuick Check:
Longest path edges = 3 [OK]
- Counting nodes instead of edges for diameter
- Ignoring parent check in DFS
- Mixing up max1 and max2 updates
def dfs(node, parent):
max_depth = 0
for child in tree[node]:
if child != parent:
depth = dfs(child, node)
if depth > max_depth:
max_depth = depth
return max_depth + 1
max_diameter = 0
def find_diameter(node, parent):
global max_diameter
depths = []
for child in tree[node]:
if child != parent:
depths.append(dfs(child, node))
if len(depths) >= 2:
depths.sort(reverse=True)
max_diameter = max(max_diameter, depths[0] + depths[1])
for child in tree[node]:
if child != parent:
find_diameter(child, node)
find_diameter(1, -1)
print(max_diameter)Solution
Step 1: Identify the conditional update bug
The code only updates max_diameter if len(depths) >= 2, but to mimic standard algorithm, it should always update using the two largest depths (with second as 0 if fewer children).Step 2: Understand impact on chain trees
In chain trees, nodes often have at most 1 child (excluding parent), so the condition skips updates, leaving max_diameter = 0 incorrectly.Final Answer:
Only updates max_diameter when len(depths) >= 2, missing updates for nodes with fewer than 2 children -> Option DQuick Check:
Pad missing depths with 0 for diameter update [OK]
- Not updating global max_diameter
- Incorrect sorting order of depths
- Missing recursive calls in diameter function
tree = {
1: [(2, 3), (3, 2)],
2: [(1, 3), (4, 4)],
3: [(1, 2)],
4: [(2, 4)]
}
# Which code snippet correctly computes diameter?Solution
Step 1: Understand weighted tree diameter
Diameter is longest weighted path, so weights must be included in depth calculations.Step 2: Modify DFS to return max weighted depth and update diameter
DFS returns max weighted depth from node; diameter updated by sum of two largest weighted depths.Final Answer:
Use dfs to return max weighted depth, update diameter by sum of top two weighted depths -> Option BQuick Check:
Weighted diameter = sum of max weighted depths [OK]
- Ignoring weights and treating as unweighted
- Using BFS without weights for weighted diameter
- Summing all weights instead of longest path
Ready to prove it?
Pass the test and this pattern moves unknown
