代码随想录算法训练营第21天

今日任务

● 530.二叉搜索树的最小绝对差 ● 501.二叉搜索树中的众数 ● 236. 二叉树的最近公共祖先

链接:https://docs.qq.com/doc/DUHl2SGNvZmxqZm1X (opens in a new tab)

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

自己想法

搜索树的最小差一定在中序遍历相邻两节点间产生, 而且只需后比前即可,因此用一个pre记录前一个节点,然后中序遍历时比较即可。

class Solution {
    public int min;
    public TreeNode pre;
    public int getMinimumDifference(TreeNode root) {
        this.min = -1;
        this.pre = null;
        invert(root);
        return this.min;
    }
    // 1.返回:无
    public void invert(TreeNode node) {
        // 2.空节点结束
        if(node == null) return;
        // 3.递归逻辑
        // 左:
        invert(node.left);
        // 中:如果有上一个元素,那就比一比
        if(this.pre != null) {
            int now_diff = node.val - this.pre.val;
            if(this.min != -1) this.min = Math.min(this.min, now_diff);
            else this.min = now_diff;
        }
        this.pre = node;
        // 右:
        invert(node.right);
    }
}

501.二叉搜索树中的众数

自己想法

搜索树的众数一定在中序遍历连续,因此用一个pre记录前一个节点,看一不一样。 同时记录当前元素出现频次以及最大频次,如果相等就加入结果集,如果大于就清空结果集,如果小于就不管。

class Solution {
    public TreeNode pre = null;
    public int freq = 1;
    public int max_freq = 1;
    public List<Integer> ans;
    public int[] findMode(TreeNode root) {
        this.ans = new ArrayList<>();
        invert(root);
        return ans.stream().mapToInt(i -> i).toArray();
    }
    public void invert(TreeNode node) {
        if(node == null) return;
        // 左:
        invert(node.left);
        // 中:
        // 有前一个节点(说明不是第一个节点)
        if(this.pre != null) {
            // 和前一个节点值相等,频次加一
            if(node.val == this.pre.val) {
                this.freq ++;
                // 如果频次和最大频次相等,加入结果集
                if(this.freq == this.max_freq) {
                    this.ans.add(node.val);
                }
                // 如果频次大于最大频次,清空结果集,加入当前节点值,更新最大频次
                else if(this.freq > this.max_freq) {
                    this.ans.clear();
                    this.ans.add(node.val);
                    this.max_freq = this.freq;
                }
            }
            // 和前一个节点值不相等,频次重置为1
            else {
                this.freq = 1; // 频次重置为1
                // 确保不漏频次都为1的的情况
                if(this.freq == this.max_freq) this.ans.add(node.val);
            }
        }
        // 没有前一个节点,说明是第一个节点,直接加入结果集
        else {
            this.ans.add(node.val);
        }
        // 更新前一个节点
        this.pre = node;
        // 右:
        invert(node.right);
    }
}

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

自己想法(推荐看完自己的,再去看一眼题解)

后序遍历(左右中),是天然的回溯过程,用返回值表示当前节点是不是p或q的父母。 也就是在回溯时告诉自己的父母,自己是不是p或q的父母。

class Solution {
    public boolean hasFound = false;
    public TreeNode ans = null;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        invert(root, p, q);
        return this.ans;
    }
    // 返回值[]表示当前节点是不是p或q的父母,都是则为[1, 1],反之为[0, 0],只有一个则为[1, 0]或[0, 1]
    public int[] invert(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return new int[]{0, 0};
        int[] left_check = invert(root.left, p, q);
        int[] right_check = invert(root.right, p, q);
        int p_check = 0;
        if(root == p) p_check = 1;
        int q_check = 0;
        if(root == q) q_check = 1;
        if(!hasFound) {
            if(left_check[0] == 1 && left_check[1] == 1) { // 答案恰好在左child手里
                this.hasFound = true;
                this.ans = root.left;
                return new int[]{1, 1};
            }
            else if(right_check[0] == 1 && right_check[1] == 1) { // 答案恰好在右child手里
                this.hasFound = true;
                this.ans = root.right;
                return new int[]{1, 1};
            }
            else if((left_check[0] + left_check[1] + right_check[0] + right_check[1]) == 2) { // 自己在中间收渔翁之利
                this.hasFound = true;
                this.ans = root;
                return new int[]{1, 1};
            }
            else { // 在中间努力一下
                if((left_check[0] + left_check[1] + p_check + right_check[0] + right_check[1] + q_check) == 2) {
                    // 努力成功
                    this.hasFound = true;
                    this.ans = root;
                    return new int[]{1, 1};
                }
                else {
                    // 努力失败,寄希望于parent
                    return new int[]{left_check[0] + left_check[1] + p_check, right_check[0] + right_check[1] + q_check};
                }
            }
        }
        else return new int[]{1, 1};
    }
}

题解想法

思路差不多,但是精简版:直接用递归函数返回值来回传结果。 当发现当前节点就是p或q时,不必再往下递归, 直接返回当前节点,去另一边找另一个p或q即可, 找不到就说明另一个p或q就在当前节点的子树中。 也算是剪枝吧。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return invert(root, p, q);
    }
    public TreeNode invert(TreeNode root, TreeNode p, TreeNode q) {
        if(root == p || root == q || root == null) return root;
        TreeNode left = invert(root.left, p, q);
        TreeNode right = invert(root.right, p, q);
        if(left != null && right == null) return left;
        else if(left == null && right != null) return right;
        else if(left != null && right != null) return root;
        else return null;
    }
}