# 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: TreeNode) -> TreeNode:
# Helper returns (LCA, depth)
def dfs(node):
if not node:
return (None, 0)
left_lca, left_depth = dfs(node.left)
right_lca, right_depth = dfs(node.right)
if left_depth > right_depth:
return (left_lca, left_depth + 1)
elif right_depth > left_depth:
return (right_lca, right_depth + 1)
else:
return (node, left_depth + 1)
lca, _ = dfs(root)
return lca
// Definition for a binary tree node.
// struct TreeNode {
// int val;
// TreeNode *left;
// TreeNode *right;
// TreeNode(int x) : val(x), left(NULL), right(NULL) {}
// };
class Solution {
public:
pair dfs(TreeNode* node) {
if (!node) return {nullptr, 0};
auto left = dfs(node->left);
auto right = dfs(node->right);
if (left.second > right.second)
return {left.first, left.second + 1};
else if (right.second > left.second)
return {right.first, right.second + 1};
else
return {node, left.second + 1};
}
TreeNode* lcaDeepestLeaves(TreeNode* root) {
return dfs(root).first;
}
};
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private class Result {
TreeNode node;
int depth;
Result(TreeNode n, int d) { node = n; depth = d; }
}
private Result dfs(TreeNode node) {
if (node == null) return new Result(null, 0);
Result left = dfs(node.left);
Result right = dfs(node.right);
if (left.depth > right.depth)
return new Result(left.node, left.depth + 1);
else if (right.depth > left.depth)
return new Result(right.node, right.depth + 1);
else
return new Result(node, left.depth + 1);
}
public TreeNode lcaDeepestLeaves(TreeNode root) {
return dfs(root).node;
}
}
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/
var lcaDeepestLeaves = function(root) {
function dfs(node) {
if (!node) return [null, 0];
const [leftLCA, leftDepth] = dfs(node.left);
const [rightLCA, rightDepth] = dfs(node.right);
if (leftDepth > rightDepth) return [leftLCA, leftDepth + 1];
if (rightDepth > leftDepth) return [rightLCA, rightDepth + 1];
return [node, leftDepth + 1];
}
return dfs(root)[0];
};
Given the root of a binary tree, find the lowest common ancestor (LCA) of its deepest leaves.
The deepest leaves
are defined as the nodes that are at the maximum depth in the tree. The lowest common ancestor
is the lowest node in the tree that has all the deepest leaves as descendants (where we allow a node to be a descendant of itself).
Constraints:
root
of the binary tree.To solve this problem, we first need to understand what the "deepest leaves" are and how to find their lowest common ancestor. A brute-force way would be:
We use a recursive depth-first search (DFS) to traverse the tree. The key idea is to return, for each node:
null
, return (null, 0)
(no LCA, depth 0).(leftLCA, leftDepth)
.(rightLCA, rightDepth)
.leftDepth
and rightDepth
:
leftDepth > rightDepth
: the deepest leaves are in the left subtree. Return (leftLCA, leftDepth + 1)
.rightDepth > leftDepth
: the deepest leaves are in the right subtree. Return (rightLCA, rightDepth + 1)
.leftDepth == rightDepth
: deepest leaves are equally deep in both subtrees. Current node is their LCA. Return (node, leftDepth + 1)
.This approach is efficient because each node is visited once, and all information is passed up the tree without redundant computation.
Let's use this example tree:
3 / \ 5 1 / / \ 6 0 8 \ 7
7
at depth 3 (root is depth 0).7
), but since we want the LCA of all deepest leaves, and there's only one, the answer is 7
.Let's see a case with two deepest leaves:
1 / \ 2 3 / \ 4 5
4
and 5
at depth 2.1
(the root).Brute-force approach:
The problem asks for the lowest common ancestor of the deepest leaves in a binary tree. By using a bottom-up recursive approach, we can compute both the depth and the LCA in a single traversal. This avoids redundant work and makes the solution both elegant and efficient. The key insight is to pass up both the depth and the candidate LCA at each step, so we always know, for each subtree, what the answer would be if the deepest leaves were all contained in that subtree. This method achieves O(N) time and O(H) space complexity, making it well-suited for large trees.