Diameter of Binary Tree in DSA Python - Time & Space Complexity
We want to understand how the time needed to find the diameter of a binary tree grows as the tree gets bigger.
The question is: how does the work increase when the number of nodes increases?
Analyze the time complexity of the following code snippet.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def diameter_of_binary_tree(root):
diameter = 0
def depth(node):
nonlocal diameter
if not node:
return 0
left = depth(node.left)
right = depth(node.right)
diameter = max(diameter, left + right)
return 1 + max(left, right)
depth(root)
return diameter
This code finds the longest path between any two nodes in a binary tree by calculating depths and updating the diameter.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Recursive calls to visit 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, so the work grows directly with the number of nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 visits |
| 100 | About 100 visits |
| 1000 | About 1000 visits |
Pattern observation: The work grows linearly as the tree size increases.
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 more than linear time because it looks at paths between nodes."
[OK] Correct: The code visits each node once and calculates depths on the way back, so it does not repeatedly check paths, keeping the time linear.
Understanding this helps you explain how recursion can efficiently solve tree problems by visiting nodes once, a key skill in many coding challenges.
"What if we changed the code to calculate diameter by checking all pairs of nodes separately? How would the time complexity change?"
Practice
Solution
Step 1: Understand the diameter definition
The diameter is the longest path between any two nodes, not just from root to leaf.Step 2: Differentiate from height and node count
Height is longest root-to-leaf path; node count is total nodes; diameter can be longer than height.Final Answer:
The length of the longest path between any two nodes in the tree -> Option AQuick Check:
Diameter = longest path length [OK]
- Confusing diameter with height
- Thinking diameter counts nodes instead of edges
- Assuming diameter is always root to leaf
Solution
Step 1: Identify correct parameter and return types
Function takes a tree node or None and returns an integer diameter length.Step 2: Check each option's signature
def diameter(root: Optional[TreeNode]) -> int: matches expected input (node or None) and output (int). Others mismatch types.Final Answer:
def diameter(root: Optional[TreeNode]) -> int: -> Option BQuick Check:
Input node, output int diameter [OK]
- Using wrong input type like int or list
- Returning wrong type like str or TreeNode
- Missing Optional for nullable root
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def diameter(root):
diameter_val = 0
def depth(node):
nonlocal diameter_val
if not node:
return 0
left = depth(node.left)
right = depth(node.right)
diameter_val = max(diameter_val, left + right)
return max(left, right) + 1
depth(root)
return diameter_val
root = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3))
print(diameter(root))Solution
Step 1: Understand tree structure and depth calls
Tree has root 1, left subtree with nodes 2,4,5 and right subtree with node 3. Longest path is 4->2->1->3 or 5->2->1->3 with length 3 edges.Step 2: Calculate diameter_val during recursion
At node 2, left and right depths are 1 each, sum 2; at root, left depth 2, right depth 1, sum 3 updates diameter_val to 3.Final Answer:
3 -> Option CQuick Check:
Longest path edges = 3 [OK]
- Counting nodes instead of edges for diameter
- Ignoring nonlocal update of diameter_val
- Misunderstanding tree structure
def diameter(root):
diameter_val = 0
def depth(node):
if not node:
return 0
left = depth(node.left)
right = depth(node.right)
diameter_val = max(diameter_val, left + right)
return max(left, right) + 1
depth(root)
return diameter_valSolution
Step 1: Identify variable scope issue
diameter_val is assigned inside depth() but not declared nonlocal, so assignment creates local variable, not updating outer diameter_val.Step 2: Fix by declaring nonlocal
Adding 'nonlocal diameter_val' inside depth() allows updating the outer variable correctly.Final Answer:
Add 'nonlocal diameter_val' inside depth() to update outer variable -> Option DQuick Check:
Use nonlocal to modify outer variable [OK]
- Forgetting nonlocal causes no diameter update
- Using global unnecessarily
- Returning diameter_val inside depth() breaks logic
Solution
Step 1: Understand diameter path definition
Diameter is longest path between any two nodes, regardless of node children count.Step 2: Consider nodes with one child
Paths can pass through nodes with one child; these nodes do not exclude paths from consideration.Final Answer:
Diameter counts the longest path even if it passes through nodes with only one child -> Option AQuick Check:
Diameter includes all nodes on longest path [OK]
- Assuming nodes with one child are ignored
- Confusing diameter with height in skewed trees
- Thinking diameter equals leaf count
Ready to prove it?
Pass the test and this pattern moves unknown
