leetcode-236. 二叉树的最近公共祖先
class Solution {
public:
deque dqp;
deque dqq;
bool findNode(TreeNode* root, deque& dq, TreeNode* target) {
dq.push_back(root);
if(root == target) {
return true;
}
bool ret = false;
if(root->left) {
ret = findNode(root->left, dq, target);
}
if (ret) {
return true;
}
if(root->right) {
ret = findNode(root->right, dq, target);
}
if (ret) {
return true;
}
dq.pop_back();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
// 最近公共祖先
findNode(root, dqp, p);
findNode(root, dqq, q);
TreeNode* ret = NULL;
while(!dqp.empty() && !dqq.empty()) {
if (dqp.front() == dqq.front()) {
ret = dqp.front();
dqp.pop_front();
dqq.pop_front();
} else {
break;
}
}
return ret;
}
};
class Solution {
public:
TreeNode* ans;
bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
bool lresult = false;
bool rresult = false;
if (root->left) {
lresult = dfs(root->left, p, q);
}
if (root->right) {
rresult = dfs(root->right, p, q);
}
if (lresult && rresult) {
// 在当前节点的左右子树上,是旁系关系
ans = root;
} else if (lresult || rresult) {
if (root == p || root == q) {
// 在当前节点的子树上,是直系关系
ans = root;
}
}
return lresult || rresult || (root == p) || (root == q);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
};
(lresult && rresult)
左子树和右子树均包含 p 节点或 q 节点,
如果左子树包含的是 p 节点,那么右子树只能包含 q 节点,反之亦然,
因为 p 节点和 q 节点都是不同且唯一的节点,
因此如果满足这个判断条件即可说明 root 就是要找的最近公共祖先。
(lresult || rresult) && (root == p || root == q)
考虑了 root 恰好是 p 节点或 q 节点且它的左子树或右子树有一个包含了另一个节点的情况,
因此如果满足这个判断条件亦可说明 root 就是要找的最近公共祖先。
时间复杂度:O(N),其中 N 是二叉树的节点数。
二叉树的所有节点有且只会被访问一次,因此时间复杂度为 O(N)。
空间复杂度:O(N),其中 N 是二叉树的节点数。
递归调用的栈深度取决于二叉树的高度,二叉树最坏情况下为一条链,此时高度为 N。
class Solution {
public:
unordered_set us;
unordered_map um;
void dfs(TreeNode* root) {
if (root->left) {
um[root->left] = root;
dfs(root->left);
}
if (root->right) {
um[root->right] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* ans;
dfs(root); // root没有根结点
unordered_map::iterator umit = um.find(p);
while(umit != um.end()) {
us.insert(umit->first);
umit = um.find(umit->second);
}
umit = um.find(q);
while(umit != um.end()) {
if (us.count(umit->first))
return umit->first;
umit = um.find(umit->second);
}
return root;
}
};
class Solution {
public:
unordered_set us;
unordered_map um;
void dfs(TreeNode* root) {
if (root->left) {
um[root->left] = root;
dfs(root->left);
}
if (root->right) {
um[root->right] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* ans;
um[root] = NULL;
dfs(root);
unordered_map::iterator umit = um.find(p);
while(umit != um.end()) {
us.insert(umit->first);
umit = um.find(umit->second);
}
umit = um.find(q);
while(umit != um.end()) {
if (us.count(umit->first))
return umit->first;
umit = um.find(umit->second);
}
return NULL;
}
};
以 [3,5,1,6,2,0,8,null,null,7,4] p=5 q=3 为例
构造 unordered_map
5 3
1 3
6 5
2 5
0 1
0 8
7 2
4 2
以p来填充 unordered_set
5
以q来访问 unordered_set
访问 4
访问 2
访问 5 ok
则最近祖先是5
思路
用哈希表存储所有节点的父节点,然后就可以利用节点的父节点信息从 p 结点开始不断往上跳,
并记录已经访问过的节点,再从 q 节点开始不断往上跳,如果碰到已经访问过的节点,
那么这个节点就是要找的最近公共祖先。
算法
从根节点开始遍历整棵二叉树,用哈希表记录每个节点的父节点指针。
从 p 节点开始不断往它的祖先移动,并用数据结构记录已经访问过的祖先节点。
同样,再从 q 节点开始不断往它的祖先移动,如果有祖先已经被访问过,
即意味着这是 p 和 q 的深度最深的公共祖先,即 LCA 节点。
只记录需要的路径
class Solution {
public:
unordered_set us;
unordered_map um;
TreeNode* gp;
TreeNode* gq;
bool dfs(TreeNode* root) {
bool lchild = false;
bool rchild = false;
if (root->left) {
lchild = dfs(root->left);
}
if (root->right) {
rchild = dfs(root->right);
}
if (lchild && rchild) {
um[root->left] = root;
um[root->right] = root;
return true;
} else if (lchild) {
um[root->left] = root;
return true;
} else if (rchild) {
um[root->right] = root;
return true;
} else if (root == gp || root == gq) {
return true;
}
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
TreeNode* ans;
gp = p;
gq = q;
um[root] = NULL;
dfs(root);
unordered_map::iterator umit = um.find(p);
while(umit != um.end()) {
us.insert(umit->first);
umit = um.find(umit->second);
}
umit = um.find(q);
while(umit != um.end()) {
if (us.count(umit->first))
return umit->first;
umit = um.find(umit->second);
}
return NULL;
}
};
再想想,如果两个结点在同一棵子树,则直接返回辈分最大的结点;
如果两个结点在不同子树,则直接返回当前结点
class Solution {
public:
TreeNode* gp;
TreeNode* gq;
TreeNode* ans;
bool dfs(TreeNode* root) {
bool lresult = false;
bool rresult = false;
if (root->left) {
lresult = dfs(root->left);
}
if (root->right) {
rresult = dfs(root->right);
}
if (lresult && rresult) {
// p 和 q 分散在 root 的左右两侧
ans = root;
return true;
} else if (root == gp || root == gq) {
// 分布在一侧的时候,以辈分大的为主
// 递归可以实现,辈分小的元素优先改,辈分大的元素延后改
// 可以确保最后修改 ans 的是备份大的元素
// 这个 else if 要在下一个 else if 的前面
// 不然辈分小的返回后,就不会再修改 ans 了
ans = root;
return true;
} else if (lresult || rresult) {
return true;
} else {
return false;
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
gp = p;
gq = q;
dfs(root);
return ans;
}
};
class Solution {
public:
deque dq1;
deque dq2;
TreeNode* gp;
TreeNode* gq;
// 找到最近祖先
int dfs(TreeNode* root) {
dq1.push_back(root);
dq2.push_back(root);
int lresult = 0;
int rresult = 0;
if (root->left) {
lresult = dfs(root->left);
}
if (root->right) {
rresult = dfs(root->right);
}
if (((lresult | rresult) == 3) ||
(((lresult | rresult) == 2) && (root->val == gq->val)) ||
(((lresult | rresult) == 1) && (root->val == gp->val))) {
return 3;
} else if (((lresult | rresult) == 2) ||
(((lresult | rresult) == 0) && (root->val == gp->val))) {
dq2.pop_back();
return 2;
} else if (((lresult | rresult) == 1) ||
(((lresult | rresult) == 0) && (root->val == gq->val))) {
dq1.pop_back();
return 1;
} else {
dq1.pop_back();
dq2.pop_back();
return 0;
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
gp = p;
gq = q;
// 最近公共祖先
dfs(root);
TreeNode* ret = NULL;
while(!dq1.empty() && !dq2.empty()) {
if (dq1.front() == dq2.front()) {
ret = dq1.front();
dq1.pop_front();
dq2.pop_front();
} else {
break;
}
}
return ret;
}
};
#include
#include
#include
using namespace std;
int main()
{
int a = 3;
unordered_set us;
unordered_set::iterator usit;
cout << "===> before" << endl;
cout << "us's size() is " << us.size() << endl;
cout << "us's count(NULL) is " << us.count(NULL) << endl;
usit = us.find(NULL);
if (usit != us.end()) {
cout << "usit can find NULL element" << endl;
} else {
cout << "usit can't find NULL element" << endl;
}
us.insert(NULL);
cout << "===> after" << endl;
cout << "us's size() is " << us.size() << endl;
cout << "us's count(NULL) is " << us.count(NULL) << endl;
usit = us.find(NULL);
if (usit != us.end()) {
cout << "usit can find NULL element" << endl;
} else {
cout << "usit can't find NULL element" << endl;
}
return 0;
}
===> before
us's size() is 0
us's count(NULL) is 0
usit can't find NULL element
===> after
us's size() is 1
us's count(NULL) is 1
usit can find NULL element
#include
#include
#include
using namespace std;
int main()
{
int a = 3;
unordered_map um;
unordered_map::iterator umit;
cout << "===> before" << endl;
cout << "um's size() is " << um.size() << endl;
cout << "um's count(NULL) is " << um.count(NULL) << endl;
umit = um.find(NULL);
if (umit != um.end()) {
cout << "umit can find NULL element" << endl;
} else {
cout << "umit can't find NULL element" << endl;
}
um[NULL] = NULL;
cout << "===> after" << endl;
cout << "um's size() is " << um.size() << endl;
cout << "um's count(NULL) is " << um.count(NULL) << endl;
umit = um.find(NULL);
if (umit != um.end()) {
cout << "umit can find NULL element" << endl;
} else {
cout << "umit can't find NULL element" << endl;
}
return 0;
}
===> before
um's size() is 0
um's count(NULL) is 0
umit can't find NULL element
===> after
um's size() is 1
um's count(NULL) is 1
umit can find NULL element