【力扣一刷】代码随想录day21(530.二叉搜索树的最小绝对差 、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先 )

【530.二叉搜索树的最小绝对差】简单题

二叉搜索树的性质:中序遍历的数组是升序数组。

思路:通过中序遍历可以确保当前遍历的节点的val大于上一个节点的val,再计算两节点的差值,更新最小差值即可。

相似题目:98.验证二叉搜索树

方法一  递归 - 中序遍历(记录中序遍历升序数组,再获取最小差值)

class Solution {
    List<Integer> nodes = new ArrayList<>();
    public int getMinimumDifference(TreeNode root) {
        // 二叉搜索树的中序遍历是一个有序的数组,只需要统计相邻节点之间的差值,获取最小的差值即可
        inorder(root);

        // 获取升序数组中相邻节点的最小差值
        int minDelta = nodes.get(1) - nodes.get(0);
        for (int i = 1; i < nodes.size() - 1; i++){
            int delta = nodes.get(i+1) - nodes.get(i);
            if (delta < minDelta) minDelta = delta;
        }
        return minDelta;
    }

    // 递归,中序遍历,记录所有节点的值
    public void inorder(TreeNode root){
        if (root == null) return;

        inorder(root.left);
        nodes.add(root.val);
        inorder(root.right);
    }
}

方法二  递归 - 中序遍历(中序遍历节点的同时,获取最小差值)

class Solution {
    TreeNode pre = null;
    int minDelta = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        if (root == null) return 0;

        getMinimumDifference(root.left);  // 遍历左子树,更新最小差值
        // 计算当前节点与上一个遍历节点的差值,比较和更新最小差值
        if (pre != null) {
            if (root.val - pre.val < minDelta) minDelta = root.val - pre.val;
        }
        pre = root;  // 更新上一个节点
        getMinimumDifference(root.right); // 遍历右子树,更新最小差值
        return minDelta; // 返回当前节点的最小差值
    }
}

方法三  统一迭代 - 中序遍历(处理节点时,更新最小差值)

统一迭代 - 中序遍历(左中右)的入栈顺序:

cur.right -> cur -> null -> cur.left

class Solution {
    public int getMinimumDifference(TreeNode root) {
        TreeNode pre = null;
        int minDelta = Integer.MAX_VALUE;
        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        stack.push(cur);
        while (!stack.isEmpty()){
            cur = stack.pop();
            if (cur != null){
                if (cur.right != null) stack.push(cur.right); // 右
                stack.push(cur); // 中
                stack.push(null); // null
                if (cur.left != null) stack.push(cur.left); // 左
            }
            else{
                cur = stack.pop();
                if (pre != null) minDelta = Math.min(cur.val - pre.val, minDelta); // 更新最小差值  
                pre = cur; // 更新上一个遍历的节点
            }
        }
        return minDelta;
    }
}


【501.二叉搜索树中的众数】简单题

方法一  递归(任意方式遍历,记录map,遍历map获取众数)

思路:

1、递归遍历二叉树,获取map,key:root.val,value:cnt

2、遍历map,获取最高频次

3、遍历map,获取出现最高频次的节点val

4、将List转为Array并返回

class Solution {
    Map<Integer, Integer> map = new HashMap<>();

    public int[] findMode(TreeNode root) {
        // 递归获取map,key:root.val,value:cnt
        getNodes(root);

        // 获取最高频次
        int max = 0;
        for (int val: map.keySet()){
            max = Math.max(max, map.get(val));
        }

        // 获取出现最高频次的节点val
        List<Integer> resList = new ArrayList<>();
        for (int val: map.keySet()){
            if (map.get(val) == max){
                resList.add(val);
            }
        }

        // 将List转为Array
        int i = 0;
        int[] res = new int[resList.size()];
        for (int val:resList){
            res[i++] = val;
        }
        return res;
    }

    // 递归获取map,key:root.val,value:cnt
    public void getNodes(TreeNode root){
        if (root == null) return;

        getNodes(root.left);
        map.put(root.val, map.getOrDefault(root.val, 0) + 1);
        getNodes(root.right);
    }
}

方法二  递归 - 中序遍历(中序遍历的同时,获取众数)

思路:利用递归进行中序遍历,相等val的节点会连续出现。令pre指向上一个遍历的节点,当节点val发生变化时(pre.val != root.val),统计上一个节点的val出现的次数,判断这个val是否为众数。

注意:最后统计的节点val并没有判断是否为众数,因此需要在递归结束后加一次判断。

class Solution {
    TreeNode pre = null;  // 记录上一个节点
    int maxCnt = 0;  // 记录最高频次
    int cnt = 0;  // 记录某个节点的val出现的次数
    List<Integer> res = new ArrayList<>();
    public int[] findMode(TreeNode root) {
        getMode(root);
        // 最后遍历到的值出现的次数没有和maxCnt比较,要单独处理
        if (cnt >= maxCnt) {
            if (cnt > maxCnt) res.clear();
            res.add(pre.val);
        }
        int[] resArray = new int[res.size()];
        int i = 0;
        for (int x : res){
            resArray[i++] = x;
        }
        return resArray;
    }
    public void getMode(TreeNode root){
        if (root == null) return;

        // 遍历左子树,统计节点val出现的次数
        getMode(root.left);
        // 当前节点的val不等于上一个节点的val时,比较上一个节点对应的cnt和maxCnt
        if (pre != null && pre.val != root.val) {
            if (cnt >= maxCnt) {
                if (cnt > maxCnt) {
                    res.clear();
                    maxCnt = cnt;
                }
                res.add(pre.val);  // 注意:加入结果集合的是上一个节点的val
            }
            cnt = 1;
        }
        else{
            cnt++;
        }
        pre = root;
        // 遍历右子树,统计节点val出现的次数
        getMode(root.right);
    }
}

方法三  统一迭代法(中序遍历,处理节点时记录当前最高频次的val)

思路:和方法二处理当前节点的思路基本一致,统一迭代中序遍历的入栈顺序为【cur.right -> cur -> null -> cur.left】

class Solution {
    public int[] findMode(TreeNode root) {
        TreeNode pre = null;
        int cnt = 0;
        int maxCnt = 0;
        List<Integer> resList = new ArrayList<>();

        Deque<TreeNode> stack = new LinkedList<>();
        TreeNode cur = root;
        stack.push(cur);

        // 统一迭代遍历
        while(!stack.isEmpty()){
            cur = stack.pop();
            if (cur != null){
                if (cur.right != null) stack.push(cur.right);
                stack.push(cur);
                stack.push(null);
                if (cur.left != null) stack.push(cur.left);
            }
            else{
                cur = stack.pop();d
                // 处理节点,记录结果
                if (pre != null && pre.val != cur.val) {
                    if (cnt >= maxCnt){
                        if (cnt > maxCnt) {
                            resList.clear();
                            maxCnt = cnt;
                        }
                        resList.add(pre.val);
                    }
                    cnt = 1;
                }
                else {
                    cnt++;
                }
                pre = cur;
            }
        }

        // 处理最后一个节点的val
        if (cnt >= maxCnt){
            if (cnt > maxCnt) {
                resList.clear();
                maxCnt = cnt;
            }
            resList.add(pre.val);
        }
        
        // List -> Array
        int[] res = new int[resList.size()];
        for(int i = 0; i < resList.size(); i++){
            res[i] = resList.get(i);
        }

        return res;
    }
}


【236. 二叉树的最近公共祖先】中等题

思路:递归 - 后序遍历

1、确定参数和返回值:

传入当前节点和要寻找祖先的两个节点p和q,返回寻找的结果。

2、确定终止条件:

如果当前节点为null,或当前节点就是要寻找的节点,返回当前节点

3、确定单层递归逻辑(后序遍历):

  • 获取左右子树的寻找结果
  • 确定当前节点的返回结果
    • 左右都不为空,则当前节点为最近公共祖先
    • 如果左右都为空,则当前节点也为空
    • 如果只有右不为空,则返回右
    • 如果只有左不为空,则返回左
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        // 确定终止条件(如果当前节点为null,或当前节点就是要寻找的节点,返回当前节点)
        if (root == null || root == p || root == q) return root;

        // 确定单层递归逻辑(后序遍历)
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);

        if (left != null && right != null) return root;  // 左右都不为空,则当前节点为最近公共祖先
        else if (left == null && right == null) return null; // 如果左右都为空,则当前节点也为空
        else if (right != null) return right;  // 如果只有右不为空,则返回右
        else return left;  // 如果只有左不为空,则返回左
    }
}

 
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值