Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Move from brute-force thinking to an efficient approach using hash map strategy.
Given the root of a binary tree, return the lowest common ancestor of its deepest leaves.
Recall that:
0. if the depth of a node is d, the depth of each of its children is d + 1.S of nodes, is the node A with the largest depth such that every node in S is in the subtree with root A.Example 1:
Input: root = [3,5,1,6,2,0,8,null,null,7,4] Output: [2,7,4] Explanation: We return the node with value 2, colored in yellow in the diagram. The nodes coloured in blue are the deepest leaf-nodes of the tree. Note that nodes 6, 0, and 8 are also leaf nodes, but the depth of them is 2, but the depth of nodes 7 and 4 is 3.
Example 2:
Input: root = [1] Output: [1] Explanation: The root is the deepest node in the tree, and it's the lca of itself.
Example 3:
Input: root = [0,1,3,null,2] Output: [2] Explanation: The deepest leaf node in the tree is 2, the lca of one node is itself.
Constraints:
[1, 1000].0 <= Node.val <= 1000Note: This question is the same as 865: https://leetcode.com/problems/smallest-subtree-with-all-the-deepest-nodes/
Problem summary: Given the root of a binary tree, return the lowest common ancestor of its deepest leaves. Recall that: The node of a binary tree is a leaf if and only if it has no children The depth of the root of the tree is 0. if the depth of a node is d, the depth of each of its children is d + 1. The lowest common ancestor of a set S of nodes, is the node A with the largest depth such that every node in S is in the subtree with root A.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Tree
[3,5,1,6,2,0,8,null,null,7,4]
[1]
[0,1,3,null,2]
lowest-common-ancestor-of-a-binary-tree-iv)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1123: Lowest Common Ancestor of Deepest Leaves
/**
* 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 {
public TreeNode lcaDeepestLeaves(TreeNode root) {
return dfs(root).getKey();
}
private Pair<TreeNode, Integer> dfs(TreeNode root) {
if (root == null) {
return new Pair<>(null, 0);
}
Pair<TreeNode, Integer> l = dfs(root.left);
Pair<TreeNode, Integer> r = dfs(root.right);
int d1 = l.getValue(), d2 = r.getValue();
if (d1 > d2) {
return new Pair<>(l.getKey(), d1 + 1);
}
if (d1 < d2) {
return new Pair<>(r.getKey(), d2 + 1);
}
return new Pair<>(root, d1 + 1);
}
}
// Accepted solution for LeetCode #1123: Lowest Common Ancestor of Deepest Leaves
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
type pair struct {
first *TreeNode
second int
}
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
var dfs func(root *TreeNode) pair
dfs = func(root *TreeNode) pair {
if root == nil {
return pair{nil, 0}
}
l, r := dfs(root.Left), dfs(root.Right)
d1, d2 := l.second, r.second
if d1 > d2 {
return pair{l.first, d1 + 1}
}
if d1 < d2 {
return pair{r.first, d2 + 1}
}
return pair{root, d1 + 1}
}
return dfs(root).first
}
# Accepted solution for LeetCode #1123: Lowest Common Ancestor of Deepest Leaves
# 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 lcaDeepestLeaves(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
def dfs(root):
if root is None:
return None, 0
l, d1 = dfs(root.left)
r, d2 = dfs(root.right)
if d1 > d2:
return l, d1 + 1
if d1 < d2:
return r, d2 + 1
return root, d1 + 1
return dfs(root)[0]
// Accepted solution for LeetCode #1123: Lowest Common Ancestor of Deepest Leaves
struct Solution;
use rustgym_util::*;
trait Postorder {
fn postorder(&self) -> (usize, TreeLink);
}
impl Postorder for TreeLink {
fn postorder(&self) -> (usize, TreeLink) {
use std::cmp::Ordering::*;
if let Some(node) = self {
let left = &node.borrow().left;
let right = &node.borrow().right;
let (height_l, left_lca) = left.postorder();
let (height_r, right_lca) = right.postorder();
match height_l.cmp(&height_r) {
Less => (height_r + 1, right_lca),
Greater => (height_l + 1, left_lca),
Equal => (height_l + 1, self.clone()),
}
} else {
(0, None)
}
}
}
impl Solution {
fn lca_deepest_leaves(root: TreeLink) -> TreeLink {
let (_, lca) = root.postorder();
lca
}
}
#[test]
fn test() {
let root = tree!(1, tree!(2), tree!(3));
let res = tree!(1, tree!(2), tree!(3));
assert_eq!(Solution::lca_deepest_leaves(root), res);
let root = tree!(1, tree!(2, tree!(4), None), tree!(3));
let res = tree!(4);
assert_eq!(Solution::lca_deepest_leaves(root), res);
let root = tree!(1, tree!(2, tree!(4), tree!(5)), tree!(3));
let res = tree!(2, tree!(4), tree!(5));
assert_eq!(Solution::lca_deepest_leaves(root), res);
let root = tree!(
1,
tree!(2, tree!(3, None, tree!(6)), tree!(4, None, tree!(5))),
None
);
let res = tree!(2, tree!(3, None, tree!(6)), tree!(4, None, tree!(5)));
assert_eq!(Solution::lca_deepest_leaves(root), res);
}
// Accepted solution for LeetCode #1123: Lowest Common Ancestor of Deepest Leaves
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function lcaDeepestLeaves(root: TreeNode | null): TreeNode | null {
const dfs = (root: TreeNode | null): [TreeNode | null, number] => {
if (root === null) {
return [null, 0];
}
const [l, d1] = dfs(root.left);
const [r, d2] = dfs(root.right);
if (d1 > d2) {
return [l, d1 + 1];
}
if (d1 < d2) {
return [r, d2 + 1];
}
return [root, d1 + 1];
};
return dfs(root)[0];
}
Use this to step through a reusable interview workflow for this problem.
BFS with a queue visits every node exactly once — O(n) time. The queue may hold an entire level of the tree, which for a complete binary tree is up to n/2 nodes = O(n) space. This is optimal in time but costly in space for wide trees.
Every node is visited exactly once, giving O(n) time. Space depends on tree shape: O(h) for recursive DFS (stack depth = height h), or O(w) for BFS (queue width = widest level). For balanced trees h = log n; for skewed trees h = n.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Recursive traversal assumes children always exist.
Usually fails on: Leaf nodes throw errors or create wrong depth/path values.
Fix: Handle null/base cases before recursive transitions.