首页 > 试题广场 >

在二叉树中找到两个节点的最近公共祖先

[编程题]在二叉树中找到两个节点的最近公共祖先
  • 热度指数:172938 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定一棵二叉树(保证非空)以及这棵树上的两个节点对应的val值 o1 和 o2,请找到 o1 和 o2 的最近公共祖先节点。

数据范围:树上节点数满足 , 节点值val满足区间 [0,n)
要求:时间复杂度

注:本题保证二叉树中每个节点的val值均不相同。

如当输入{3,5,1,6,2,0,8,#,#,7,4},5,1时,二叉树{3,5,1,6,2,0,8,#,#,7,4}如下图所示:
所以节点值为5和节点值为1的节点的最近公共祖先节点的节点值为3,所以对应的输出为3。
节点本身可以视为自己的祖先
示例1

输入

{3,5,1,6,2,0,8,#,#,7,4},5,1

输出

3
示例2

输入

{3,5,1,6,2,0,8,#,#,7,4},2,7

输出

2

说明:本题目包含复杂数据结构TreeNode,点此查看相关信息
 
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     思路很简单,我们可以采用递归函数来解决,我们每访问一个结点,我们以该结点为出发点,默认
     两个目标还没有找到。根据当前结点查找左子节点、右子节点、自身结点。然后根据查找结果再将
     结果返回给上层结点进行更新自身的查找结果。
     */
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        if(root->val==o1||root->val==o2){
            return NULL;
        }
        // write code here
        bool findo1=false;
        bool findo2=false;
        int nearest_root = root_first(root, o1, o2,findo1,findo2);
        return nearest_root;      
    }
    int root_first(TreeNode* root,int o1,int o2,bool& findo1,bool& findo2 )
    {
        /*
        传入的findo1为引用,可以更新父结点的查找结果。
        if(root->val==o1&&!findo1){
            findo1=true;
        }
        if(root->val==o2&&!findo2){
            findo2=true;
        }
        if(findo1&&findo2){
            return NULL;
        }
        */
        int res_left = NULL;
        int res_right = NULL; 
        bool findo1_=false;
        bool findo2_ = false;
           if(root->left){           
           res_left = root_first(root->left, o1, o2,findo1_,findo2_);
           if(!findo1){
                findo1=findo1_;
            }
            if(!findo2){
                findo2=findo2_;
            }
           }    
        if(!findo1_||!findo2_){
            if(root->right){
             res_right = root_first(root->right, o1, o2,findo1_,findo2_);
               if(!findo1){
                findo1=findo1_;
            }
            if(!findo2){
                findo2=findo2_;
            }
            }
        }else{
            return res_left;
        }
        if(findo1_&&findo2_){
           if(res_right==NULL){
               return root->val;
           }else{
               return res_right;
           }         
        }else{
        if(root->val==o1&&!findo1){
            findo1_=true;
            findo1 = true;
        }
        if(root->val==o2&&!findo2){
            findo2_=true;
            findo2=true;
        }
        if(findo1_&&findo2_){
            return root->val;
        }
            return NULL;
        }
    }
};


发表于 2021-07-31 17:22:34 回复(0)
  public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        if(root==null) return -1;
        if(o1==root.val || o2==root.val) return root.val;
        int left = lowestCommonAncestor(root.left, o1, o2);
        int right = lowestCommonAncestor(root.right,o1,o2);
        if(left ==-1) return right;
        if(right ==-1)  return left;
        return root.val;
    }

编辑于 2020-09-18 11:43:01 回复(18)
关键还是找到最近公共节点的特征:
1. 如果该节点不是O1也不是O2,那么O1与O2必然分别在该节点的左子树和右子树中
2. 如果该节点就是O1或者O2,那么另一个节点在它的左子树或右子树中
稍微可以优化的一点就是,遇到O1或者O2节点就不往下递归了,把O1或者O2节点一层层往上传。
class Solution {
public:
    
    TreeNode* dfs(TreeNode* root, int p, int q) {
        if (root == nullptr || root->val == p || root->val == q) {
            return root;
        }

        TreeNode* left = dfs(root->left, p, q);
        TreeNode* right = dfs(root->right, p, q);

        return left==nullptr ? right : (right==nullptr ? left : root);
    }
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        TreeNode* res = dfs(root, o1, o2);
        return res->val;
    }
};


编辑于 2020-11-22 16:11:52 回复(1)
相信大家不愿意看递归的版本,我也不愿意看,就写了一个非递归的
步骤,
先来一遍中序遍历,我们知道,中序遍历会以根节点为中心,将左右子树分成两半,
然后利用给定的两个值o1,o2,然后找出他们俩的下标,如果两个下标一个在左,一个在右,
则此根节点就是最近公共祖先,如果这两下标均在根节点的一侧,则更新根节点,继续上述步骤
    //需要一个全局的list来存放中序遍历的结果
    ArrayList<Integer> list=new ArrayList<>();
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        midOrders(root);
        //用一个hashmap来记录每一个结点值对应的下标
        HashMap<Integer, Integer> map = new HashMap<>();
        //为每一个结点值配好下标
        for(int i=0;i<list.size();i++){
            map.put(list.get(i),i);
        }
        while (root!=null){
            //找出当前根节点的下标
            int k=map.get(root.val);
            //判断查询的两个值在根节点的哪一边
            if(map.get(o1)<k&&map.get(o2)<k){
                root=root.left;
            }else if(map.get(o1)>k&&map.get(o2)>k){
                root=root.right;
            }else{
                return root.val;
            }
        }
        return -1;
    }
    public void midOrders(TreeNode root){
        if(root==null){
            return ;
        }
        midOrders(root.left);
        list.add(root.val);
        midOrders(root.right);
    }

发表于 2021-04-29 10:58:45 回复(3)
public int lowestCommonAncestor(TreeNode root, int o1, int o2) {
        if(root == null) return -1; // 如果树为空,直接返回null
        if(root.val == o1 || root.val == o2) return root.val; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
        int left = lowestCommonAncestor(root.left, o1, o2); // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
        int right = lowestCommonAncestor(root.right, o1, o2); // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
        if(left == -1) return right; // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else if(right == -1) return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else return root.val; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
    }
发表于 2021-08-22 21:05:29 回复(1)
最简单的代码:
class Solution:
    def lowestCommonAncestor(self , root , o1 , o2 ):
        # write code here
        if root == None:
            return -1
        if root.val==o1 or root.val==o2:
            return root.val
        if  self.lowestCommonAncestor(root.left, o1, o2)==-1:
            return self.lowestCommonAncestor(root.right, o1, o2)
        if  self.lowestCommonAncestor(root.right, o1, o2)==-1:
            return self.lowestCommonAncestor(root.left, o1, o2)
        return root.val
点我头像,有惊喜哦

编辑于 2021-07-16 15:57:33 回复(4)
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        // write code here
        if (root == nullptr) return -1;
        if (root->val == o1 || root->val == o2) return root->val;
        
        int left = lowestCommonAncestor(root->left, o1, o2);
        int right = lowestCommonAncestor(root->right, o1, o2);
        
        if (left != -1 && right != -1) return root->val;
        else if (left != -1) return left;
        else if (right != -1) return right;
        else return -1;
    }
};

发表于 2020-09-15 22:13:31 回复(4)
主要还是靠递归来解决问题。
1.构造判断某节点是否是o1或者o2的祖宗的函数。
2.从树的根结点开始,如果它的左孩子或者右孩子即是o1的祖宗,也是o2的祖宗,那么
公共祖先可以往下推。否则它本身就是最近公共祖先。

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        // write code here
        if(root==NULL || !IsAncestor(root, o1) || !IsAncestor(root, o2)) return -1;
        if(root->left && IsAncestor(root->left, o1) && IsAncestor(root->left, o2) ) 
            return  lowestCommonAncestor(root->left,o1,o2);
        if(root->right && IsAncestor(root->right, o1) && IsAncestor(root->right, o2) )
            return  lowestCommonAncestor(root->right,o1,o2);
        return root->val;
    }
    
    bool IsAncestor(TreeNode *root,int val){
        if(root==NULL) return false;
        if(root->val==val) return true;
        return IsAncestor(root->left,val) || IsAncestor(root->right,val);
    }
    
};


发表于 2020-09-05 17:27:40 回复(5)

来个通俗易懂的解法(非递归):
1.分别采用非递归后序遍历找到要寻找的节点o1,o2,并且返回遍历栈s1,s2
2.遍历栈中s1,s2从前往后数,最后一个相同的节点就是最近的公共祖先
3.由于栈是先进后出,因此需要把栈内的元素放到队列中,放到队列中挨个数即可。

class Solution {
public:
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        stack s1 = postOrder(root,  o1);
        stack s2 = postOrder(root,  o2);
        vector v1,v2;
        int res;
        queue q1,q2;
        TreeNode* p;
        int size1 = s1.size();
        int size2 = s2.size();
        while(!s1.empty()){
            p = s1.top();
            q1.push(p);
            s1.pop();
        }
        while(!s2.empty()){
            p = s2.top();
            q2.push(p);
            s2.pop();
        }
        queue deleteQueue= size1>size2?q1:q2;
         queue saveQueue = size1>size2?q2:q1;
        for(int i =0;i<abs(size1-size2);++i){
            deleteQueue.pop();
        }
        int r1,r2;
        while(!deleteQueue.empty()&&!saveQueue.empty()){
             r1 =saveQueue.front()->val;
             r2 =deleteQueue.front()->val;
            if(r1==r2){return r1;}
             deleteQueue.pop();
             saveQueue.pop();

        }
        return 0;
    }
      stack postOrder(TreeNode* root,int num){
        stack nodeStack;
        TreeNode * pointer = root;
        TreeNode * pre = root;
        while(pointer != nullptr){
            while(pointer -> left != nullptr){
                nodeStack.push(pointer);
                pointer = pointer -> left;
            }
            while(pointer != nullptr && (pointer -> right == nullptr ||pre == pointer->right)){
                if(num==pointer->val){
                    nodeStack.push(pointer);
                    return nodeStack;
                }
                pre = pointer;
                pointer = nodeStack.top();
                nodeStack.pop();
            }
            nodeStack.push(pointer);
            pointer = pointer -> right;
        }
        return nodeStack;
    }

};
发表于 2021-09-11 12:53:50 回复(0)
function lowestCommonAncestor( root ,  o1 ,  o2 ) {
    // write code here
    if(root == null) return -1;
    if(root.val == o1 || root.val == o2) return root.val;
    let left = lowestCommonAncestor(root.left, o1,o2);
    let right = lowestCommonAncestor(root.right, o1, o2);
    if(left == -1) return right;
    if(right == -1) return left;
    return root.val;
}
module.exports = {
    lowestCommonAncestor : lowestCommonAncestor
};
发表于 2021-09-04 10:39:09 回复(0)
public class Solution {
//用来记录树中所有节点对应的的父节点
    HashMap<Integer, Integer> map = new HashMap<>();
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        List<Integer> list = new ArrayList<>();
        dfs(root);
        list.add(o1);
        int val1 = map.get(o1);
        while(map.get(val1) != null){
            val1 = map.get(val1);
            list.add(val1);
        }
        if(list.contains(o2)){
            return o2;
        }
        while(map.get(o2) != null){
             o2 = map.get(o2);
             if(list.contains(o2)){
                return o2;
            }
        }
        return -1;
    }
    
    public void dfs(TreeNode root){
        if(root.left != null){
            map.put(root.left.val, root.val);
            dfs(root.left);
        }
        if(root.right != null){
            map.put(root.right.val, root.val);
            dfs(root.right);
        }
    }
}

编辑于 2020-09-10 16:44:07 回复(6)
import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        return process(root, o1, o2).ans.val;
    }
    
    public Info process(TreeNode node, int o1, int o2) {
        if (node == null) {
            return new Info(null, false, false);
        }
        Info leftInfo = process(node.left, o1, o2);
        Info rightInfo = process(node.right, o1, o2);
        TreeNode ans = null;
        boolean findP = o1 == node.val || leftInfo.findP || rightInfo.findP;
        boolean findQ = o2 == node.val || leftInfo.findQ || rightInfo.findQ;
        if (leftInfo.ans != null) {
            ans = leftInfo.ans;
        }
        if (rightInfo.ans != null) {
            ans = rightInfo.ans;
        }
        if (ans == null) {
            if (findP && findQ) {
                ans = node;
            }
        }
        return new Info(ans, findP, findQ);
    }
    
    class Info {
        TreeNode ans;
        boolean findP;
        boolean findQ;
        
        Info (TreeNode ans, boolean findP, boolean findQ) {
            this.ans = ans;
            this.findP = findP;
            this.findQ = findQ;
        }
    }
}

发表于 2021-10-05 21:31:44 回复(1)
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        if(root.val == o1 || root.val == o2){
            return root.val;
        }
        TreeNode treeNode = order(root, o1, o2);
        return treeNode.val;
    }
    public TreeNode order(TreeNode root, int o1, int o2){
        if(root == null)
            return null;
        if(root.val == o1 || root.val == o2)
            return root;
        TreeNode left = order(root.left, o1, o2);
        TreeNode right = order(root.right, o1, o2);
        
        if(left != null && right != null)
            return root;
        if(left == null && right == null)
            return null;
        
        return left == null? right: left;
    }

发表于 2021-07-30 10:13:57 回复(0)
class Solution:
    def lowestCommonAncestor(self , root , o1 , o2 ):
        # write code here
        '''
        思路很简单,遇到这种题一定要想简单思路。
        我们定义子树或本身中包含o1或o2的节点为公共节点。距离根最远的公共节点就是我们要求的最近公共祖先。
        1. 如果当前节点是第一个包含o1或o2的节点,那么当前节点一定是公共节点,但不一定是最近公共祖先,直接将其向上回溯即可
        2.否则就去看该节点的左右子树里找
            若左子树里找到了公共节点且右边没有,那就将左边找到的公共节点向上回溯
            若右子树里找到了公共节点且左边没有,那就将右边找到的公共节点向上回溯
            若左右子树中都有公共节点,那说明当前根是最近公共祖先,将其向上返回即可。
        '''
        
        def dfs(root,o1,o2):
            if not root&nbs***bsp;root.val == o1&nbs***bsp;root.val == o2:
                return root
            left = dfs(root.left,o1,o2)
            if not left:return dfs(root.right,o1,o2)
            right = dfs(root.right,o1,o2)
            if not right:return left
            return root
        return dfs(root,o1,o2).val 

发表于 2021-07-18 21:06:40 回复(1)
public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        TreeNode res = dfs(root, o1, o2);
        return res.val;
    }
    public TreeNode dfs(TreeNode root, int o1, int o2) {
        if(root == null || root.val == o1 || root.val == o2) {
            return root;
        }
        TreeNode left = dfs(root.left, o1, o2);
        TreeNode right = dfs(root.right, o1, o2);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}

发表于 2021-06-03 15:36:11 回复(0)
class Solution:
    def lowestCommonAncestor(self , root , o1 , o2 ):
        # write code here
        return self.dfs(root, o1, o2).val
    
    def dfs(self, root, o1, o2):
        if not root or root.val == o1 or root.val == o2:
            return root
        
        left = self.dfs(root.left, o1, o2)
        right = self.dfs(root.right, o1, o2)
        if left is None:
            return right
        if right is None:
            return left
        
        return root

编辑于 2021-04-05 22:40:54 回复(0)
后序遍历
    public int lowestCommonAncestor(TreeNode root, int o1, int o2) {
        Stack<TreeNode> s1 = find(root, o1);
        Stack<TreeNode> s2 = find(root, o2);
        Set<TreeNode> set1 = new HashSet<>(s1);
        while (!s2.isEmpty()) {
            if (set1.contains(s2.peek())) return s2.pop().val;
            s2.pop();
        }
        return -1;
    }

    private Stack<TreeNode> find(TreeNode root, int o) {
        Stack<TreeNode> s1 = new Stack<>();
        Stack<Boolean> s2 = new Stack<>();
        TreeNode p = root;
        while (p != null || !s1.isEmpty()) {
            if (p != null) {
                s1.push(p);
                s2.push(true);
                p = p.left;
                continue;
            }
            p = s1.pop();
            if (s2.pop()) {
                s1.push(p);
                s2.push(false);
                p = p.right;
            } else {
                if (p.val == o) {
                    s1.push(p);
                    return s1;
                }
                p = null;
            }
        }
        return s1;
    }


DFS
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        TreeNode node = find(root,o1,o2);
        return node == null ? null:node.val;
    }
    
    public TreeNode find (TreeNode root, int o1, int o2) {
        if(root == null || root.val == o1 || root.val == o2) return root;
        TreeNode left = find(root.left,o1,o2);
        TreeNode right = find(root.right,o1,o2);
        if(left != null && right != null) return root;
        return left != null ? left:right;
    }


发表于 2021-03-14 22:15:26 回复(0)
//解题思路:遇到o1或者o2就不往下遍历了,直接向上返回值
import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 * }
 */

public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        
        if(root==null) return -1;
        if(root.val==o1||root.val==o2) return root.val;
        int left=lowestCommonAncestor(root.left,o1,o2);
        int right=lowestCommonAncestor(root.right,o1,o2);
        if(left==-1) return right;
        if(right==-1) return left;
        return root.val;
    }
}
发表于 2021-03-13 15:51:29 回复(0)
【递归】C++或Python版:
基于公共祖先特点的递归做法
从根节点往下递归:
1. 若该节点是第一个值为o1或o2的节点,则该节点是最近公共祖先;
2. 否则,看左子树是否包含o1或o2:
    2.1 若左子树包含o1或o2,则看右子树有没有:
        a. 若右子树没有,则公共祖先在左子树
        b. 若右子树有,则o1和o2肯定是左右子树一边一个,则公共祖先是根节点;
    2.2 若左子树不包含o1和o2,则公共祖先在右子树或者该根子树不包含o1和o2。(两种情况都取决于右子树)

C++代码:
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */

class Solution {
public:
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */
    int lowestCommonAncestor(TreeNode* root, int o1, int o2) {
        // write code here
        return dfs(root,o1,o2)->val;
    }
    TreeNode* dfs(TreeNode* root, int o1,int o2){
        // 最近的节点
        if(root == nullptr || root-> val == o1 || root->val == o2) return root;
        
        // 如果左子树o1和o2都没有,则可能在右子树
                // 或者左右子树都没有,则root子树也没有
        TreeNode* left = dfs(root->left,o1,o2);
        if(left == nullptr) return dfs(root->right,o1,o2);
        
        // 如果左子树有o1或者o2,看右子树有没有
        TreeNode* right = dfs(root->right,o1,o2);
        if(right == nullptr) return left; // 如果右子树没有,则左子树肯定同时包含o1和o2
        
        // 如果左右子树都有,则肯定是一边一个
        return root;
    }
};
Python代码:

# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

#
# 
# @param root TreeNode类 
# @param o1 int整型 
# @param o2 int整型 
# @return int整型
#
class Solution:
    def lowestCommonAncestor(self , root , o1 , o2 ):
        # write code here
        return self.dfs(root,o1,o2).val
        
    # 该子树是否包含o1或o2
    def dfs(self,root,o1,o2):
        if root is None: 
            return  None
            
        if root.val == o1&nbs***bsp;root.val == o2: 
            return root
            
        left = self.dfs(root.left,o1,o2)
        # 左子树没有,则在右子树
                # 若右子树没有,则右子树返回 None
        if left == None: return self.dfs(root.right,o1,o2)

        # 左子树有,则看右子树有没有
        right = self.dfs(root.right,o1,o2)
        if right == None : return left
        
        # 左子树右子树都有,则最近的祖先节点是root
        return root
        
        
编辑于 2021-01-02 21:54:48 回复(3)
遇到null直接返回-1感觉还是不太好(难道返回一个随机的大数?),最好还是另写一个可以返回TreeNode的dfs
发表于 2020-12-14 10:16:36 回复(1)