在二叉树(搜索树)中找到两个节点的最近公共祖先(剑指offer)

举报
bug郭 发表于 2022/10/06 21:52:09 2022/10/06
【摘要】 二叉搜索树的最近公共祖先题目链接 \//方法一:递归!public int lowestCommonAncestor (TreeNode root, int p, int q) { // write code here //利用二叉搜索树的特性,左子树小于根,根小于右子树! //从而定位到公共祖先节点! ...

二叉搜索树的最近公共祖先

题目链接

image-20220623104756281 \

//方法一:递归!
public int lowestCommonAncestor (TreeNode root, int p, int q) {
            // write code here
            //利用二叉搜索树的特性,左子树小于根,根小于右子树!
            //从而定位到公共祖先节点!
            if(root==null) return -1;
            //pq在该节点两边,所以 root就是公共节点!
            if(root.val>=p&&root.val<=q||root.val<=p&&root.val>=q) return root.val;
            //pq都在该节点左边,说明在左子树
            if(root.val>=q&&root.val>=p)
                return lowestCommonAncestor(root.left,p,q);
            else{
                return lowestCommonAncestor(root.right,p,q);
            }
}
  • 时间复杂度O(N)最坏情况递归遍历完所有节点!
  • 空间复杂度O(N),递归深度最坏为O(N)

alt

  • 通过两个数组集合保存两个节点的路径值
  • 找到最后一个相等的节点,即是公共祖先!
//  
public ArrayList<Integer> getPath(TreeNode root,int x){
        ArrayList<Integer> path = new ArrayList<>();
        if(root==null) return path;
        while(root.val!=x){//节点值不同,说明还没遍历到 x!
            path.add(root.val);
            //这里不能用两个if判断,因为这里判断一次就会改变,root的指向!!!
            if(root.val>x){
                //说明在左子树!
                root = root.left;
            }else{
                //说明在右子树!
                root = root.right;
            }
        }
       //将 x 节点保存!
       path.add(x);
        return path;
    }
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        // write code here
        ArrayList<Integer> path1 = getPath(root,p);
        ArrayList<Integer> path2 = getPath(root,q);
        int ret = 0;
        for(int i = 0;i<path1.size()&&i<path2.size();i++){
            //这里的 path1.get(i)不能直接比较相等,包装类需要通过equals比较!
            if(path1.get(i).equals(path2.get(i))){
                ret = path2.get(i);//更新相同值
            }else{
                break;//无相同值,退出循环!
            }
        }
        return ret;
    }

注意:

Integer包装类比较不能直接通过==比较,要使用equals比较!

  • 时间复杂度: O(N)最坏情况,遍历完所有节点,也就是二叉树单分支,变成了链表,所以需要比较所有节点!
  • 空间复杂度:O(N)最坏情况,所有节点都记录保存!

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

题目链接

描述:

image-20220624130527609

  • 这里和上面的搜索二叉树不同,不能直接利用搜索二叉树的特性,直接找到该路径!

  • 我们这里只是普通的二叉树,需要遍历所有的节点,然后将其路径找出!

  • 而我们知道找路径就是找到他这一条路径的祖先,也就是说将每个节点的父节点关系保存即可!

  • 我们借助Map键值对保存,key保存当前节点,value保存根节点!

  • 这里我们可以借助队列遍历到o1o2节点就即可!

//方法一:BFS 广度优先!
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        //借助一种数据结构保存o1和o2的路径!
        //我们知道这里的二叉树只是普通的二叉树,并不是二叉搜索
        //所以无法直接定位到节点路径
        //我们的路径无法是该节点的所有父节点,那么我们可以换种思路!
        //通过map记录o1和o2节点的父节点,以及o1和o2的一系列祖宗,通过map键值对映射保存!
        //然后我们在找出o1的路径即可!
        //然后再在o1路径中找到o2或者o2的最近祖先即是最近公共祖先!
        if(root==null) return -1;
        Map<Integer,Integer> map = new HashMap<>();//key保存当前节点,value保存父节点!
        map.put(root.val,Integer.MAX_VALUE);//根节点无父节点!
        //通过队列,进行层序遍历,当遍历到o1和o2节点就退出!
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        while(!map.containsKey(o1)||!map.containsKey(o2)){
            TreeNode cur = q.poll();
            if(cur.left!=null){
                 //保存当前节点父子关系!
                map.put(cur.left.val,cur.val);
                q.offer(cur.left);
            }
            if(cur.right!=null){
                map.put(cur.right.val,cur.val);
                q.offer(cur.right);
            }
        }
        //将o1和其祖宗路径保存!
        Set<Integer> ancestors = new HashSet<>();
        while(map.containsKey(o1)){
            ancestors.add(o1);
            //找到其父亲,一直到根节点!
            o1 = map.get(o1);
        }
        while(!ancestors.contains(o2)){
            //在o1祖宗中找到o2或者o2的最近祖先
            o2 = map.get(o2);
        }
        return o2;
    }

时间复杂度:O(N) 最坏所有节点都递归遍历一遍!
空间复杂度:O(N) mapqueue分别最坏O(N)


我们可以采取递归的方式!

  • o1o2分别位于root的左右子树上

image-20220624132242936

  • o1==root,o2在其子树上!

    image-20220624132359650

    • o2==root,o2在其子树

    image-20220624132601267

//方法二:递归
 public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
        // write code here
        if(root == null) return -1;
        //找到最近公共节点!
        return help(root,o1,o2).val;
    }
    public TreeNode help(TreeNode root,int o1,int o2){
        if(root==null||root.val==o1||root.val==o2){
            //递归到空||找到了o1或者o2
            return root;
        }
        //看左子树是否存在o1或者o2节点
        TreeNode left = help(root.left,o1,o2);
        TreeNode right = help(root.right,o1,o2);
        if(left==null){
            //左子树为空,说明o1和o2节点在右子树!
            return right;
        }
        if(right==null){
            return left;
        }
        //说明o1和o2分别在两边!
        return root;
    }

时间复杂度:O(N) 最坏所有节点都递归遍历一遍!
空间复杂度:O(N) 单分支树,退化成了链表!

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。