随想录Day20|今日内容 ● 530.二叉搜索树的最小绝对差 ● 501.二叉搜索树中的众数 ● 236. 二叉树的最近公共祖先

530. 二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

输入:root = [4,2,6,1,3]
输出:1

示例 2:

输入:root = [1,0,48,null,null,12,49]
输出:1

提示:

  • 树中节点的数目范围是 [2, 10^4]
  • 0 <= Node.val <= 10^5

思路:

要写这题首先需要对平衡二叉树有一个比较好的理解,只有这样才能一眼看出本题实际上解题的遍历方式就是中序遍历。

因为平衡二叉树节点的性质是左子树的所有元素大于节点元素,右子树的所有元素大于节点元素,根据这一性质特点,选择中序遍历便可以让整体遍历的元素单调递增,这样问题便也转化为了如何在一个元素排列从小到大的数组里找出最小的差值,相信大家看到这种题目一定就能马上写出来了。

如果说在数组里寻找最小差值的话,首先需要一个保存最小值的元素min,然后是两个一前一后的指针left,right,其中left = right-1,用于对比前后两个数的差值。

但是还有一个问题就是,我们现在操作的可不是数组,二叉树怎么实现这样一前一后的指针呢?当然,你也可以先遍历一遍形成一个数组,然后再对这个数组进行求值,不过这样就慢了,浪费了时间也浪费了空间,因为在二叉树中使用双指针是可以一次遍历求出答案的

详细操作的说明请看我代码的备注

    TreeNode pre ;
    int min ;
    public int getMinimumDifference(TreeNode root) {
        //思路:双指针,一个指向当前节点,一个指向前一个节点
        //初值赋最大,好进行比较
        min = Integer.MAX_VALUE;
        pre = null;
        inorder(root);
        return min;

    }
    public void inorder(TreeNode root){
        //左中右
        if(root==null)return;
        inorder(root.left);//左
        //中
        //下面的这段代码,我们先对中节点进行操作,之后再对pre指针进行赋值,紧接着再遍历下一个元素
        //这样便能实现将pre指针滞留在前一个节点的效果了
        if(pre!=null && min>Math.abs(root.val- pre.val)){
            min = Math.abs(root.val- pre.val);
            pre = root;
        }else pre = root;
        inorder(root.right);//右
    }

501. 二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

示例 1:

输入:root = [1,null,2,2]
输出:[2]

示例 2:

输入:root = [0]
输出:[0]

思路:

这一题很朴素的想法就是建立一个HashMap,键值队以<元素,出现频率>的形式记录下来,随后进行遍历将所有元素放入map中,再创建一个新数组取出这些元素并排好序,最后得出结果,我第一遍就是这么干的。

这样可以吗?当然可以的,对所有的二叉树都能这样求出众数,但是却没有充分利用题目所给的条件

题目给出的树是一个拥有相同元素的搜索二叉树,那么我们用中序遍历的结果依然会得到一个单调递增的数组,不过会有一些值重复罢了。

那么问题也就变成了如何在一个元素从小到大的数组中求出里面的众数,其实也是用到了双指针的思想

我们需要创建一个数组res接受结果,定义一个count记录当前元素重复的次数,定义一个maxCount记录出现频率最多元素出现的次数

当count==maxCount时就将元素添加到数组

当count>maxCount就把当前数组清空,更新数据使maxCount = count,并将当前元素添加入数组

如此循环往复便可实现一次遍历得出结果

代码如下:

//正确的解法:双指针+中序遍历
    //因为中序遍历的元素是升序的,所以说问题就变成了在一个排好序的数组里
    //怎么通过一次遍历就把里面的众数求出来
        TreeNode pre;
        int maxCount;
        int count;
        List<Integer> res;
        public int[] findMode(TreeNode root) {
            pre = null;
            maxCount =0;
            count =0;
            res= new ArrayList<>();
            inorder(root);
            System.out.println(res);
            int[] arr = new int[res.size()];
            for(int i=0;i<res.size();i++){
                arr[i] = res.get(i);
            }
            return arr;
        }
        public void inorder(TreeNode root){
            if (root == null) {
                return;
            }
            inorder(root.left);//左
            //处理逻辑
            int rootValue = root.val;
            // 计数
            if (pre == null || rootValue != pre.val) {
                count = 1;
            } else {
                count++;
            }
            //根据count的值进行处理
            if(count >maxCount){

                res.clear();
                res.add(rootValue);
                maxCount = count;
            }
            //count==maxCount
            else if(count==maxCount){
                res.add(rootValue);
            }
            pre = root;// 更新上一个节点
            inorder(root.right);//右
        }

236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1

思路:

看到这一类题目我们首先要思考遍历方式,显而易见的是想要求出答案我们必须事先清楚子节点的情况,所以我们采用后序遍历

那么答案会有几种情况呢,我认为是两种

1.节点p,q在同一棵子树上,并且二者的祖先是二者之一

2.节点p,q处于不同的子树上,二者的祖先处于树的更高的高度

代码如下

TreeNode res;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        res = null;
        isExist(root,p,q);
        return res;

    }
    public boolean isExist(TreeNode root, TreeNode p, TreeNode q){
        //思路:用后续遍历
        //情况一:节点p,q都在同一颗子树上,且祖先是二者之一
        //情况二:节点p,q处于不同的子树上,二者的祖先在更高的高度上
        //结束条件
        if(root == null)return false;
        boolean left= isExist(root.left,p,q);
        boolean right= isExist(root.right,p,q);
        //中

        //情况一
        //当前节点
        if((left||right)&&(root==p||root==q)){
            res = root;
            return true;
        } else if (root == p||root==q) {
            return true;
        }
        //子节点
        else if(left&&right){res = root;return true;}//情况二
        else if(left||right){
            return true;
        }
        return false;

    }

值得一提的是我代码中对于当前节点与子节点的处理顺序,我认为要先处理当前节点再处理子节点,

因为情况一是需要通过当前节点判断得出的答案,而情况二是通过子节点返回值判断得出的答案,如果先判断子节点的结果的话很容易就漏掉了上当前节点上的答案,最后得不出结果,这一点是需要大家注意的.

感谢大家观看!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值