Loading problems...
TL;DR: We solve this by using a Postorder DFS traversal to calculate the height of each node's subtrees, updating the global maximum diameter (left height + right height) as the recursion bubbles up.
The Diameter of Binary Tree problem asks us to find the length of the longest path between any two nodes in a binary tree. The length is defined by the number of edges, not nodes. Importantly, the longest path does not necessarily have to pass through the root of the tree; it can exist entirely within a left or right subtree.
This is a classic LeetCode 543 problem that tests your understanding of tree traversals and recursion state management, making it a popular interview question at major tech companies.
A naive approach relies on the definition that the diameter passing through a specific node is the sum of the maximum depth of its left subtree and the maximum depth of its right subtree.
To solve this via brute force, we could visit every node in the tree. For each node, we would trigger a separate helper function to calculate the maximum depth of its left and right children. We would then sum these depths to find the diameter anchored at that node and compare it to a global maximum.
Pseudo-code:
text
function diameter(root):
if root is null: return 0
// Calculate depth of left and right subtrees explicitly
leftHeight = getDepth(root.left)
rightHeight = getDepth(root.right)
// Diameter passing through current root
currentDiameter = leftHeight + rightHeight
// Recurse on children to find if a larger diameter exists elsewhere
leftDiameter = diameter(root.left)
rightDiameter = diameter(root.right)
return max(currentDiameter, leftDiameter, rightDiameter)
function getDepth(node):
if node is null: return 0
return 1 + max(getDepth(node.left), getDepth(node.right))Time Complexity Analysis:
The time complexity of this approach is in the worst case (a skewed tree). This is because for every node visited by diameter, we traverse its entire subtree again to calculate getDepth.
diameter visits nodes.getDepth takes time.Why it fails: While correct, is inefficient. With constraints up to nodes, this approach performs redundant calculations. We are recalculating the depth of the same nodes repeatedly. An optimal interview solution requires .
The key intuition for optimizing the LeetCode 543 solution is that the height (or depth) of a node and the diameter passing through that node can be computed in the same traversal.
Every node in a binary tree can be viewed as the "turning point" (or anchor) of a path. The longest path passing through a specific node u is simply the longest leg extending down its left child plus the longest leg extending down its right child.
Instead of calculating depth separately, we can design a dfs function that:
left_height + right_height) and updates a global maximum.By using Postorder Traversal (Left Right Root), we ensure that when we are at a specific node, we already know the heights of its left and right subtrees. This allows us to compute the diameter for that node in time and return the height to the parent, resulting in a single pass.
Visual Description: Imagine the recursion tree expanding until it hits the leaf nodes. As the recursion unwinds (returns), values flow upward. A leaf node reports a height of 1 to its parent. The parent receives heights from both left and right children. It adds them together to check the "local diameter" and updates the global maximum if this sum is the largest seen so far. Then, it adds 1 to the larger of the two child heights and passes that value up to its own parent.

max_diameter initialized to 0. This tracks the longest path found anywhere in the tree.node as input.
node is null, return 0. This represents a height of 0.node.left and store the result in left_height.node.right and store the result in right_height.node: current_diameter = left_height + right_height.max_diameter = max(max_diameter, current_diameter).1 + max(left_height, right_height).max_diameter holds the answer.Let's trace the execution for root = [1, 2, 3, 4, 5].
DFS(2) and DFS(3).
2. Call DFS(2): Need DFS(4) and DFS(5).
3. Call DFS(4):
* Left child is null (returns 0).
* Right child is null (returns 0).
* Update max_diameter: max(0, 0+0) = 0.
* Return height: 1 + max(0, 0) = 1.
4. Call DFS(5):
* Left child is null (returns 0).
* Right child is null (returns 0).
* Update max_diameter: max(0, 0+0) = 0.
* Return height: 1 + max(0, 0) = 1.
left_height (from 4) is 1.right_height (from 5) is 1.1 + 1 = 2.max_diameter: max(0, 2) = 2.1 + max(1, 1) = 2.max_diameter: max(2, 0) = 2.1.left_height (from 2) is 2.right_height (from 3) is 1.2 + 1 = 3.max_diameter: max(2, 3) = 3.3.Final result is 3.
The correctness relies on the property that the longest path in a binary tree must have a unique "highest" node (the node closest to the root that is part of the path). This node acts as the turning point connecting a path in its left subtree and a path in its right subtree.
Our algorithm iterates through every node in the tree using DFS. At every node , we calculate the length of the longest path where is the highest node (height(u.left) + height(u.right)). Since we check this value for every node and maintain the maximum, we are guaranteed to find the true global diameter.
cpp/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { private: int maxDiameter = 0; // Helper function returns the height (max depth) of the tree rooted at node int calculateHeight(TreeNode* node) { if (node == nullptr) { return 0; } // Postorder traversal: visit children first int leftHeight = calculateHeight(node->left); int rightHeight = calculateHeight(node->right); // Update the global maximum diameter // Diameter at this node = left height + right height maxDiameter = std::max(maxDiameter, leftHeight + rightHeight); // Return the height of the current node to the parent return 1 + std::max(leftHeight, rightHeight); } public: int diameterOfBinaryTree(TreeNode* root) { calculateHeight(root); return maxDiameter; } };
java/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { private int maxDiameter = 0; public int diameterOfBinaryTree(TreeNode root) { calculateHeight(root); return maxDiameter; } // Returns the height (number of nodes on longest path down) private int calculateHeight(TreeNode node) { if (node == null) { return 0; } // Recursively find heights of left and right subtrees int leftHeight = calculateHeight(node.left); int rightHeight = calculateHeight(node.right); // Update the diameter passing through the current node maxDiameter = Math.max(maxDiameter, leftHeight + rightHeight); // Return the height of this node to be used by the parent return 1 + Math.max(leftHeight, rightHeight); } }
python# Definition for a binary tree node. # class TreeNode: # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution: def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: self.max_diameter = 0 def height(node): if not node: return 0 # Recursive postorder traversal left_height = height(node.left) right_height = height(node.right) # The path through this node is left_height + right_height self.max_diameter = max(self.max_diameter, left_height + right_height) # Return height of the current node return 1 + max(left_height, right_height) height(root) return self.max_diameter
Time Complexity: We visit every node exactly once using the Depth First Search traversal. The operations performed at each node (addition, max comparison) are constant time . Thus, the total time is linear with respect to the number of nodes .
Space Complexity: The space complexity is determined by the recursion stack. In the worst case (a skewed tree like a linked list), the recursion depth can reach . In the best case (a balanced tree), the depth is . Since we must account for the worst case, the space complexity is .
The Tree DFS - Recursive Postorder Traversal pattern is highly versatile. Understanding how to bubble up information from children to parents applies directly to:
Why does my solution get Time Limit Exceeded (TLE)?
Why is my output 1 greater than expected?
Why is my answer wrong for skewed trees?
height(root.left) + height(root.right) at the very end, ignoring larger diameters found deeper in the recursion. You must update a global maximum at every node.