LeetCode 236. 二叉树的最近公共祖先
https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
题目
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 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。因为根据定义最近公共祖先节点可以为节点本身。
题解
思想类似于Tarjan算法求LCA,但是该题只需要找到一对点的LCA,所以用栈来记录搜索过的结点,不需要记录父亲结点,也就省去了并查集的操作。
栈内的每个格子记录结点的内容和结点状态
状态分为:
编号 | 状态 | |
---|---|---|
0 | bothpending | 左右儿子都没有搜索过 |
1 | leftdone | 左儿子已经搜索过 |
2 | bothdone | 左右儿子都已经搜索过 |
算法过程:
temp:栈顶元素,res:可能的答案,要找p与q的lca
- 从根节点开始入栈
- temp=栈顶元素,栈pop
- 如果 temp为p或q中的一个且是第一次找到,那么res=temp
- 如果temp为p或q中的另一个,res就是lca,break;
- 如果temp的状态是0(bothpending),将temp的状态改为1(leftdone),入栈。进行左儿子搜索,把左儿子的状态改为0(bothdone),入栈。
- 如果temp的状态是1(leftdone),将temp的状态改为2(bothdone),入栈。进行右儿子搜索,把右儿子的状态改为0(bothdone),入栈。
- 如果temp的状态是2(bothdone)(这是难点)。此时栈顶元素是temp的父亲结点,如果res=temp,要把res转移为temp的父亲结点,即res=栈顶。
下面用具体例子表示
找9和11的lca
struct TreeNode
{
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution
{
public:
struct node
{
TreeNode* a;
int b;//0 Bothpending ,1 leftdone,2 bothdone
node(){}
node(TreeNode* root,int x):a(root),b(x){}
};
stack<node>s;
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
TreeNode* res=nullptr;
s.push(node(root,0));
int ff=0;
while(!s.empty())
{
node temp;
temp=s.top();s.pop();
if((temp.a==p||temp.a==q)&&ff==0)
{
res=temp.a;ff=1;
}
else if((temp.a==p||temp.a==q)&&ff==1&&res!=temp.a)
{
break;
}
if(temp.b==0)
{
if(temp.a->left!=NULL)
{
s.push(node(temp.a,1));
s.push(node(temp.a->left,0));
}
else s.push(node(temp.a,1));
}
else if(temp.b==1)
{
if(temp.a->right!=NULL)
{
s.push(node(temp.a,2));
s.push(node(temp.a->right,0));
}
else s.push(node(temp.a,2));
}
else if(temp.b==2)
{
if(temp.a==res)
{
res=s.top().a;
}
}
}
return res;
}
};