一、题目描述
二、解题思路
这是剑指Offer的最后一道题,本质是二叉树的后序遍历(非递归)。牢牢记住二叉树的后序遍历以非递归形式遍历到某一节点时,辅助栈内保存的元素即为根节点到该节点的路径。
知道了上面的性质后,我们就可以利用这两个性质来做题
- 创建三个栈:遍历栈
s
,保存pp
的路径的栈sp
和保存qq
的路径的栈sq
- 遍历到当前节点时,调用拷贝构造函数,把遍历栈
s
拷贝给sp
或sq
- 当两个待查找节点全都被遍历到后,马上停止遍历
- 但是栈内保存的路径是反过来的,最底层栈元素才是根节点,而且路径栈内不保存当前待查节点。而实际上是存在公共祖先为节点本身的这种情况,那么我们就应该知道一定从那个最近公共祖先开始,两个路径到根节点的路径都相同,所以我们将两个栈内保存的路径加上当前节点都转移到
vector
中去,注意最开始要先加入当前节点,以解决公共祖先为节点本身的这种情况。之后我们反向遍历两个链表,找到最后一个一样的元素即可
三、核心思想
- 非递归
- 二叉树的后序遍历以非递归形式遍历到某一节点时,辅助栈内保存的元素即为根节点到该节点的路径
- 递归
- root为空,p,q为root时 直接返回root
- 递归左右,都不为空说明一个在左,一个在右 此时返回root
- 此时谁不为空返回谁
四、解题代码
非递归
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* pp, TreeNode* qq) {
if(!root) return root;
auto p = root;
TreeNode* r = nullptr;
stack<TreeNode*> s, sq, sp;
unsigned short flag = 0;
while(p || !s.empty()){
if(p){
s.push(p);
p = p->left;
}
else{
p = s.top();
if(p->right && p->right != r)
p = p->right;
else{
s.pop();
if(p == pp){
sp = s;
flag++;
}
else if(p == qq){
sq = s;
flag++;
}
if(flag == 2) break;
r = p;
p = nullptr;
}
}
}
vector<TreeNode*> vq, vp;
vq.push_back(qq);
vp.push_back(pp);
while(!sq.empty()){
vq.push_back(sq.top());
sq.pop();
}
while(!sp.empty()){
vp.push_back(sp.top());
sp.pop();
}
int i, j;
TreeNode* sln;
for(i = vp.size() - 1, j = vq.size() - 1; i >= 0 && j >= 0 && vp[i] == vq[j]; i--, j--) sln = vp[i];
return sln;
}
};
递归
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root || p == root || q == root) return root;
auto l = lowestCommonAncestor(root->left, p, q);
auto r = lowestCommonAncestor(root->right, p, q);
if(l && r) return root;
return l ? l : r;
}
};
五、运行结果
递归
自己写的测试用例
[1,2,3,4,5,6,7,8,9,10,11,12,null,null,null,13,14,15,16,null,17,null,null,18,19,20,21,22,23,null,null,null,null,24,25,null,null,null,null,null,null,null,null]