二叉树的最近公共祖先
先看题目描述:
这道题目我给出两种写法:
思路一:
运用后序遍历的回溯过程,如果找到了我想要的节点就像上层返回该节点,没有找到就返回空。首先可以观察到如果一个节点在root的左子树,另一个在右子树,root就是公共祖先;如果两个都在root的左(右)子树,则公共祖先是其中一颗子树返回的节点。
解题步骤见下图:
针对于上图中第三种特殊情况,我们不用关心p节点是否能找到,因为递归到了q就会向上返回,并且q就是我们要的最近公共祖先。
下面给出了C++实例代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == nullptr) return nullptr; // 遇到了空节点就说明没找到, 返回空
if(root == p || root == q) return root; // 遇到p或q向上返回对应的节点
TreeNode* left = lowestCommonAncestor(root->left, p, q); // 左子树给根节点的返回值
TreeNode* right = lowestCommonAncestor(root->right, p, q);// 右子树给根节点的返回值
if(left == nullptr && right != nullptr)
return right;
else if(left != nullptr && right == nullptr)
return left;
else if(left != nullptr && right != nullptr)
return root;
else
return nullptr;
}
};
复杂度分析:
时间复杂度 O(N) : 其中 N 为二叉树节点数;最坏情况下,二叉树近似于链式结构,需要递归遍历树的几乎所有节点。
空间复杂度 O(N) : 最差情况下,递归深度达到 N ,需要使用 O(N) 大小的额外空间。
思路二:
DFS求出从根节点到p和q的路径放在容器中,转换成路径相交问题。
具体解题步骤如下图:首先遍历二叉树,将一路上遇到的不等于p或q的节点统统入栈,直到遇到p或q就向上层返回。可能有些人会有疑问,那不在p或q路径上的节点不是我们想要的,如何出栈呢?
对于上面图的5节点的左子树-6节点而言,它的左右子树均未找到p和q,于是将它pop掉;然后到了5的右子树,5的右子树先 DFS 2的左子树-7节点,刚好7节点就是q,注意不用去 DFS 2的右子树了,直接向上返回true,此时栈上保存的数据就是从根节点到q的路径。结合代码去理解:
bool Getpath(TreeNode* root, TreeNode* n, stack<TreeNode*>& path)
{
if(root == nullptr) return false;
// 将路过的节点都入栈
path.push(root);
if(root == n) return true;
// 没找到就继续往下找
if(Getpath(root->left, n, path)) return true;
if(Getpath(root->right, n, path)) return true;
// 左右子树都遍历完了都没找到
path.pop();
return false;
}
将p和q的路径保存到栈之后,下面就是要想办法找到最近公共节点。
这种思想和leetcode160.相交链表的思想是一样的。
第一步就是让快指针从长度较长的链表头结点先走gap步,gap为两链表长度的差值的绝对值;在这道题里,我们将栈里面元素最多的那个栈一直pop,直到两栈元素相等。
stack<TreeNode*> ppath, qpath;
while(ppath.size() != qpath.size())
{
if(ppath.size() > qpath.size())
ppath.pop();
else
qpath.pop();
}
第二步,让长链表上的快指针和短链表上的慢指针一起走,直到遇到相等节点。在这里,将两栈里面元素pop,直到两栈顶元素相等。
while(ppath.top() != qpath.top())
{
ppath.pop();
qpath.pop();
}
return ppath.top();
示例代码如下:
bool Getpath(TreeNode* root, TreeNode* n, stack<TreeNode*>& path)
{
if(root == nullptr) return false;
// 将路过的节点都入栈
path.push(root);
if(root == n) return true;
// 没找到就继续往下找
if(Getpath(root->left, n, path)) return true;
if(Getpath(root->right, n, path)) return true;
// 左右子树都遍历完了都没找到
path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> ppath, qpath;
Getpath(root, p, ppath);
Getpath(root, q, qpath);
while(ppath.size() != qpath.size())
{
if(ppath.size() > qpath.size())
ppath.pop();
else
qpath.pop();
}
while(ppath.top() != qpath.top())
{
ppath.pop();
qpath.pop();
}
return ppath.top();
}