一、题目
题目链接:力扣
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。
二、题解
1、思路
首先需要看上一题!
寻找最近祖先肯定要确定p、q在最近祖先的子树中,显然我们不能像上一题那样,根据二叉搜索树的性质直接判断p、q是否存在于子树当中,因此只能通过遍历二叉树的方式寻找,显然不能从上往下寻找(因为从上往下找,不知道后面节点是什么)
💌 dfs递归
定义 f_x 表示 x 节点的子树中是否包含 p 节点或 q 节点,如果包含为 true,否则为 false。那么符合条件的最近公共祖先 x 一定满足如下条件:
(f_lson && f_rson) || ( (x=p || x=q) && (f_lson || f_rson) )
1. (f_lson && f_rson)
说明左子树和右子树均包含 p 节点或 q 节点,如果左子树包含的是 p 节点,那么右子树只能包含 q 节点,反之亦然。因为 p 节点和 q 节点都是不同且唯一的节点,因此如果满足这个判断条件即可说明 x 就是我们要找的最近公共祖先。
2. ( (x=p || x=q) && (f_lson || f_rson) )
因为 p 节点和 q 节点都是不同且唯一的节点,因此如果满足这个判断条件即可说明 x 就是我们要找的最近公共祖先。
定义一个函数 dfs 递归遍历整棵二叉树,dfs函数:
- 如果是最近祖先(满足1、2),记录x。
- 如果不是最近祖先,那么dfs函数返回当前子树是否包含p、q节点的信息;(返回值)
- 递归左子树和右子树;(递推)
- 当前节点为空,返回false;(截止条件)
2、代码实现
💌 dfs递归
需要后续遍历:因为我们需要看当前root左右子树是否含有p、q,只有后续遍历才能满足。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);// 调用深度优先搜索函数,搜索整个二叉树
return res;
}
private:
TreeNode* res = NULL;// 定义一个指针变量用于保存最近公共祖先节点
// 用于判断node为根节点的树是否包含p、q
bool dfs(TreeNode* node, TreeNode* p, TreeNode* q)
{
if(node == NULL)return false;// 不满足p、q祖先情况
bool left = dfs(node->left, p, q);// 递归搜索左子树,并得到左子树是否包含目标节点的信息
bool right = dfs(node->right, p, q);// 递归搜索右子树,并得到右子树是否包含目标节点的信息
// 如果左右子树都包含了目标节点,或者当前节点为目标节点且其左右子树中至少有一个包含了目标节点,则当前节点为最近公共祖先
if((left && right) || ((node->val == p->val || node->val == q->val) && (left || right)) )res = node;
// 返回当前子树是否包含目标节点的信息
// 左子树包含p或者q,右子树包含p或者q,或者当前根节点等于p或q
return left || right || (node->val == p->val || node->val == q->val); }
};
3、复杂度分析
💌 dfs递归
时间复杂度:O(n);二叉树的所有节点有且只会被访问一次;
空间复杂度:O(n)。
4、运行结果
💌 dfs递归