方法一:直接递归
1) 如果当前结点 root 等于NULL,则直接返回NULL
2) 如果 root 等于 p 或者 q ,那这棵树一定返回 p 或者 q
3) 然后递归左右子树,因为是递归,使用函数后可认为左右子树已经算出结果,用 left和 right 表示
4) 此时若left为空,那最终结果只要看 right;若 right为空,那最终结果只要看 left
5) 如果 left和 right 都非空,因为只给了 p 和 q 两个结点,都非空,说明一边一个,因此 root是他们的最近公共祖先
6) 如果 left 和 right 都为空,则返回空(其实已经包含在前面的情况中了)
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(!root) return nullptr;
if(root==p||root==q)
return root;
TreeNode* left=lowestCommonAncestor(root->left,p,q);
TreeNode* right=lowestCommonAncestor(root->right,p,q);
if(!right) return left;//右递归为空,返回左(可能为空)
if(!left) return right;
return root;//左右都不为空
}
};
方法二:使用父指针迭代
1)从根节点开始先序遍历树。
2)在找到 p 和 q 之前,将父指针存储在字典中。
3)一旦我们找到了 p 和 q,我们就可以使用父指针字典获得 p 的所有祖先,并添加到一个称为祖先的集合中(set)。
4)同样,我们遍历节点 q 的祖先。如果祖先存在于为 p 设置的祖先中,这意味着这是 p 和 q 之间的第一个公共祖先(同时向上遍历),因此这是 LCA 节点。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
Deque<TreeNode> stack = new ArrayDeque<>();
Map<TreeNode, TreeNode> parent = new HashMap<>();
parent.put(root, null);
stack.push(root);
while (!parent.containsKey(p) || !parent.containsKey(q)) {
TreeNode node = stack.pop();
if (node.right != null) {
parent.put(node.right, node);
stack.push(node.right);
}
if (node.left != null) {
parent.put(node.left, node);
stack.push(node.left);
}
}
Set<TreeNode> ancestors = new HashSet<>();
while (p != null) {
ancestors.add(p);
p = parent.get(p);
}
while (!ancestors.contains(q))
q = parent.get(q);
return q;
}
}
方法三:记录路径
1)用两个数组分别记录p、q路径找;
2)找到两个数组第一个不相同节点的前一个节点,就是公共祖先。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
vector<TreeNode*> path_a, path_b;
if (!get_path(root, p, path_a) || !get_path(root, q, path_b))
return nullptr;
int i = 1;
for (; i < path_a.size() && i < path_b.size(); i++) {
if (path_a[i] != path_b[i]) return path_a[i-1];
}
return path_a[i-1];
}
bool get_path(TreeNode* root, TreeNode* a, vector<TreeNode*>& path) {
if (!root) return false;
path.emplace_back(root);
if (root == a) return true;
if (get_path(root->left, a, path)) return true;
if (get_path(root->right, a, path)) return true;
path.pop_back();
return false;
}
};
参考:leetcode解法