【BST】235. 二叉搜索树的最近公共祖先

题目描述

(简单)给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 pq,最近公共祖先表示为一个结点 x,满足 xpq 的祖先且 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

解题思路

本题是一道典型的二叉排序树搜索题!虽然经过题目的包装,但也要保持对其二叉排序树本质的敏锐性。

给出结论,从根节点出发,第一次访问的在pq节点之间的节点即为最近公共祖先

为什么一定是第一次呢?下面从正反两种思路论证:

  1. 正向思考,先不考虑最近,想一想什么是公共祖先?祖先,父节点、父节点的父节点等等,即从根节点出发,沿二叉树搜索路径上所有目标节点的上级节点。公共,即节点p与节点q路径上的重合节点。此时考虑最近,即重合路径上的最后一个节点,出现分叉的节点!那么,什么时候路径会分叉呢?当目标区间小于当前节点时,从该节点出发的路径只有一条,右转,同理目标区间大于当前节点。故只有在目标区间内,才会产生分歧。区间的开闭如何考虑?鉴于一个节点也可以是它自己的祖先,故为左闭右闭的闭区间。
  2. 反过来思考,最近公共祖先是第一次访问区间内节点与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);
    }
};

运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值