二叉树最近公共祖先(LCA)

在这里插入图片描述

方法一:直接递归

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解法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值