题目描述
(简单)给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p
、q
,最近公共祖先表示为一个结点 x
,满足 x
是 p
、q
的祖先且 x
的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
示例:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。
解题思路
本题是一道典型的二叉排序树搜索题!虽然经过题目的包装,但也要保持对其二叉排序树本质的敏锐性。
给出结论,从根节点出发,第一次访问的在p
、q
节点之间的节点即为最近公共祖先。
为什么一定是第一次呢?下面从正反两种思路论证:
- 正向思考,先不考虑最近,想一想什么是公共祖先?祖先,父节点、父节点的父节点等等,即从根节点出发,沿二叉树搜索路径上所有目标节点的上级节点。公共,即节点
p
与节点q
路径上的重合节点。此时考虑最近,即重合路径上的最后一个节点,出现分叉的节点!那么,什么时候路径会分叉呢?当目标区间小于当前节点时,从该节点出发的路径只有一条,右转,同理目标区间大于当前节点。故只有在目标区间内,才会产生分歧。区间的开闭如何考虑?鉴于一个节点也可以是它自己的祖先,故为左闭右闭的闭区间。 - 反过来思考,最近公共祖先是第一次访问区间内节点与
p/q
节点之间的节点可能吗?考虑到二叉排序树的特性:左根右,左子树的最大值小于根节点,右子树的最小值大于根节点。因此,将第一次访问区间内的节点视为根节点,其将目标区间分割成两个区域,大于根节点与小于根节点。进一步想,根节点左子树下节点的右子树,理论上无法到达目标区间的右端点!同理,根节点右子树下节点的左子树,理论上无法到达目标区间的左端点。因此,既然无法到达p/q
种某一节点,怎么能是公共祖先呢?更不必考虑最近!
理清楚上述逻辑后,本体的简单性暴露无遗,直接上代码!
代码实现
/**
* 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:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
//空指针直接返回,避免或许逻辑上抛出空指针异常
if(!root) return root;
//确保p为区间左端点,q为区间右端点
if(p->val > q->val) swap(p, q);
//访问到区间内端点立即返回
if(root->val >= p->val && root->val <= q->val) return root;
//向右搜索
if(root->val < p->val) return lowestCommonAncestor(root->right, p, q);
//向左搜索
else return lowestCommonAncestor(root->left, p, q);
}
};
运行结果: