Diameter Of a Binary Tree
Given the root
of a binary tree, return the length of the diameter of the tree.
The diameter of a binary tree is the length of the longes path between any two nodes in a tree. This path may or may not pass through the root
.
Examples:
Example 1:
Input: root = [1, 2, 3, 4, 5]
Output: 3
Example 2:
Input: root = [1, 2]
Output: 1
here trees are represented in array in a bfs order where all members of layer n are listed before all members of layer n+1
Solution 1: Naive Recursive
We implement a recursive solution where the only parameter is the root node. Note that in our main funciton, we need to track the longest diameter of the left child and the longest diameter of the right child.
To help with our solution, we implement two helper functions which are also recursive.
Implementation:
class Solution{
private:
int longestLeftPath(TreeNode* root){
if(root == nullptr || root->left == nullptr){
return 0;
}
return 1 + max(longestLeftPath(root->left), longestRightPath(root->right));
}
int longestRightPath(TreeNode* root){
if(root == nullptr || root->right == nullptr){
return 0;
}
return 1 + max(longestLeftPath(root->left), longestRightPath(root->right));
}
public:
int diameterOfBinaryTree(TreeNode* root){
if(root == nullptr) return 0;
return max(longestLeftPath(root) + longestRightPath(root),
max(diameterOfBinaryTree(root->left), diameterOfBinarytree(root->right))
);
}
}
Complexity analysis:
Time Complexity:
The time complexity of longestLeftPath
and longestRightPath
is \(O(n)\) because we do \(o(1)\) work per call and have a max of n calls.
The time complexity of diameterOfBinaryTree
is \(O(n)\) time for the longestLeftPath
and longestRightPath
calls and because of recursion, these calls can happen a maximum of \(n\) times.
So the overall time complexity is \(O(n^2)\) due to our helper functions.
The overall space complexity is \(O(n^2)\) because recursive calls happen a maximum of \(n^2\) times in total.
Solution 2: Updating value while recursing
To save runtime, we combine our previous functions into one recursive function. However, we need some form of memory, because it may be the case that the left height + the right height is less than the diameter of either the left subtree or the right subtree alone. Therefore, we will carry our best previous diameter within a pointer diameter
and update it whenever we have a new greatest diameter.
Implementation:
class Solution{
private:
//height measures the distance from the root to a leaf
int heightWithDia(TreeNode* root, int& diameter){
if(root=nullptr) return 0;
int lh = heightWithDia(TreeNode* root->left, diameter);
int rh = heightWithDia(TreeNode* root->right, diameter);
//crucial to have a max here, because in some cases lh will be 0 or 1 while lh or rh of the children is high.
diameter = max(diameter, lh + rh);
return max(lh + 1, rh + 1);
}
public:
int diameterOfBinaryTree(TreeNode* root){
int dia = 0;
heightWithDia(root, dia);
return dia;
}
}
Complexity Analysis
Time Complexity:
The time complexity of heightWithDia
is \(O(n)\). We do \(O(1)\) work a maximum of \(n\) times.
The overall time and space complexity is \(O(n)\) because all we have to do is initialize and return a variable dia
.