Trees
Java solutions with explanations, time and space complexity for Trees problems from Blind 75.
Trees
This section covers problems involving binary trees, binary search trees, and tree traversal algorithms.
1. Invert Binary Tree (Easy)
Problem: Given the root
of a binary tree, invert the tree, and return its root.
Example:
Input: root = [4,2,7,1,3,6,9]
Output: [4,7,2,9,6,3,1]
Solution:
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
// Invert left and right subtrees
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
// Swap left and right children
root.left = right;
root.right = left;
return root;
}
}
Time Complexity: O(n) Space Complexity: O(h) where h is the height of the tree
2. Maximum Depth of Binary Tree (Easy)
Problem: Given the root
of a binary tree, return its maximum depth.
A binary tree’s maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
Example:
Input: root = [3,9,20,null,null,15,7]
Output: 3
Solution:
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
int leftDepth = maxDepth(root.left);
int rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
}
Time Complexity: O(n) Space Complexity: O(h)
3. Same Tree (Easy)
Problem: Given the roots of two binary trees p
and q
, write a function to check if they are the same or not.
Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.
Example:
Input: p = [1,2,3], q = [1,2,3]
Output: true
Solution:
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
return p.val == q.val &&
isSameTree(p.left, q.left) &&
isSameTree(p.right, q.right);
}
}
Time Complexity: O(n) Space Complexity: O(h)
4. Subtree of Another Tree (Easy)
Problem: Given the roots of two binary trees root
and subRoot
, return true
if there is a subtree of root
with the same structure and node values of subRoot
and false
otherwise.
Example:
Input: root = [3,4,5,1,2], subRoot = [4,1,2]
Output: true
Solution:
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (subRoot == null) return true;
if (root == null) return false;
if (isSameTree(root, subRoot)) return true;
return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
private boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q == null) return true;
if (p == null || q == null) return false;
return p.val == q.val &&
isSameTree(p.left, q.left) &&
isSameTree(p.right, q.right);
}
}
Time Complexity: O(n * m) where n and m are the number of nodes in root and subRoot Space Complexity: O(h)
5. Lowest Common Ancestor of a Binary Search Tree (Medium)
Problem: Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST.
Example:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Solution:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) return null;
// If both p and q are less than root, LCA is in left subtree
if (p.val < root.val && q.val < root.val) {
return lowestCommonAncestor(root.left, p, q);
}
// If both p and q are greater than root, LCA is in right subtree
if (p.val > root.val && q.val > root.val) {
return lowestCommonAncestor(root.right, p, q);
}
// If p and q are on different sides, root is LCA
return root;
}
}
Time Complexity: O(h) Space Complexity: O(h)
6. Binary Tree Level Order Traversal (Medium)
Problem: Given the root
of a binary tree, return the level order traversal of its nodes’ values. (i.e., from left to right, level by level).
Example:
Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]
Solution:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if (root == null) return result;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int levelSize = queue.size();
List<Integer> currentLevel = new ArrayList<>();
for (int i = 0; i < levelSize; i++) {
TreeNode node = queue.poll();
currentLevel.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
result.add(currentLevel);
}
return result;
}
}
Time Complexity: O(n) Space Complexity: O(n)
7. Validate Binary Search Tree (Medium)
Problem: Given the root
of a binary tree, determine if it is a valid binary search tree (BST).
Example:
Input: root = [2,1,3]
Output: true
Solution:
class Solution {
public boolean isValidBST(TreeNode root) {
return isValidBST(root, null, null);
}
private boolean isValidBST(TreeNode root, Integer min, Integer max) {
if (root == null) return true;
if ((min != null && root.val <= min) ||
(max != null && root.val >= max)) {
return false;
}
return isValidBST(root.left, min, root.val) &&
isValidBST(root.right, root.val, max);
}
}
Time Complexity: O(n) Space Complexity: O(h)
8. Kth Smallest Element in a BST (Medium)
Problem: Given the root
of a binary search tree, and an integer k
, return the kth
smallest value (1-indexed) of all the values of the nodes in the tree.
Example:
Input: root = [3,1,4,null,2], k = 1
Output: 1
Solution:
class Solution {
private int count = 0;
private int result = 0;
public int kthSmallest(TreeNode root, int k) {
inorder(root, k);
return result;
}
private void inorder(TreeNode root, int k) {
if (root == null) return;
inorder(root.left, k);
count++;
if (count == k) {
result = root.val;
return;
}
inorder(root.right, k);
}
}
Time Complexity: O(k) Space Complexity: O(h)
9. Construct Binary Tree from Preorder and Inorder Traversal (Medium)
Problem: Given two integer arrays preorder
and inorder
where preorder
is the preorder traversal of a binary tree and inorder
is the inorder traversal of the same tree, construct and return the binary tree.
Example:
Input: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
Output: [3,9,20,null,null,15,7]
Solution:
class Solution {
private int preorderIndex = 0;
private Map<Integer, Integer> inorderMap = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
inorderMap.put(inorder[i], i);
}
return buildTreeHelper(preorder, 0, inorder.length - 1);
}
private TreeNode buildTreeHelper(int[] preorder, int left, int right) {
if (left > right) return null;
int rootValue = preorder[preorderIndex++];
TreeNode root = new TreeNode(rootValue);
int inorderIndex = inorderMap.get(rootValue);
root.left = buildTreeHelper(preorder, left, inorderIndex - 1);
root.right = buildTreeHelper(preorder, inorderIndex + 1, right);
return root;
}
}
Time Complexity: O(n) Space Complexity: O(n)
10. Binary Tree Maximum Path Sum (Hard)
Problem: A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root.
The path sum of a path is the sum of the node’s values in the path.
Given the root
of a binary tree, return the maximum path sum of any non-empty path.
Example:
Input: root = [1,2,3]
Output: 6
Solution:
class Solution {
private int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
private int maxGain(TreeNode root) {
if (root == null) return 0;
int leftGain = Math.max(maxGain(root.left), 0);
int rightGain = Math.max(maxGain(root.right), 0);
int priceNewPath = root.val + leftGain + rightGain;
maxSum = Math.max(maxSum, priceNewPath);
return root.val + Math.max(leftGain, rightGain);
}
}
Time Complexity: O(n) Space Complexity: O(h)
11. Serialize and Deserialize Binary Tree (Hard)
Problem: Design an algorithm to serialize and deserialize a binary tree.
Example:
Input: root = [1,2,3,null,null,4,5]
Output: [1,2,3,null,null,4,5]
Solution:
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "null";
return root.val + "," + serialize(root.left) + "," + serialize(root.right);
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
Queue<String> queue = new LinkedList<>(Arrays.asList(data.split(",")));
return deserializeHelper(queue);
}
private TreeNode deserializeHelper(Queue<String> queue) {
String val = queue.poll();
if (val.equals("null")) return null;
TreeNode root = new TreeNode(Integer.parseInt(val));
root.left = deserializeHelper(queue);
root.right = deserializeHelper(queue);
return root;
}
}
Time Complexity: O(n) Space Complexity: O(n)
Key Takeaways
- Tree Traversal: Master inorder, preorder, and postorder traversals
- Recursion: Most tree problems can be solved recursively
- BST Properties: Use BST properties for efficient searching
- Level Order: Use BFS for level-order traversal
- Path Problems: Consider both single path and combined path scenarios
- Serialization: Use special markers for null nodes