代码随想录算法训练营第三期day21-二叉树07

目录

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

1.1 法1、递归

1.1.1 版本Ⅰ、直观版

1.1.2 版本Ⅱ、最终版

1.2 法2、迭代

1.2.1 版本Ⅰ、普通迭代

1.2.2 版本Ⅱ、统一迭代

1.3 总结

2. T501:二叉搜索树中的众数

2.1 法1、递归

2.1.1 版本Ⅰ:for普通二叉树

2.1.2 版本Ⅱ:二叉搜索树

2.2 法2、迭代

3. T236:二叉树的最近公共祖先


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

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

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

提示:

  • 树中节点的数目范围是 [2, 104]

  • 0 <= Node.val <= 105

S:

还是跟day20的最后一题:验证二叉搜索树一样,先从最直观的思路入手,不断优化~

1.1 法1、递归

1.1.1 版本Ⅰ、直观版

C++:

    vector<int> vec;
    int getMinimumDifference(TreeNode* root) {
        if (!root) return 0;
        traversal(root);
        if (vec.size() < 2) return 0;
        int diff= INT_MAX;
        for (int i = 1; i < vec.size(); ++i) {
            if (res > vec[i] - vec[i - 1]) {
                res = vec[i] - vec[i - 1];
            }
        }
        return diff;
    }
    void traversal(TreeNode* node) {
        if (!node) return;
        traversal(node->left);
        // if (node->left && diff > node->val - node->left->val) {
        //     diff = node->val - node->left->val;
        // }//这样只能比较本节点及其左子节点,比如这个例子就会导致错误:[1,null,2]->INT_MAX
        vec.push_back(node->val);
        traversal(node->right);
    }

Java:

    List<Integer> nums;
    public int getMinimumDifference(TreeNode root) {
        if (root == null) return 0;
        nums = new ArrayList<>();
        traversal(root);
        int diff = Integer.MAX_VALUE;
        for (int i = 1; i < nums.size(); ++i) {
            if (diff > nums.get(i) - nums.get(i - 1)) {
                diff = nums.get(i) - nums.get(i - 1);
            }
        }
        return diff;
    }
    void traversal(TreeNode root) {
        if (root == null) return;
        traversal(root.left);
        nums.add(root.val);
        traversal(root.right);
    }

1.1.2 版本Ⅱ、最终版

C++:

    int diff = INT_MAX;
    TreeNode* pre = nullptr;
    int getMinimumDifference(TreeNode* root) {
        if (!root) return 0;
        traversal(root);
        return diff;
    }
    void traversal(TreeNode* node) {
        if (!node) return;
        traversal(node->left);
        if (pre && diff > node->val - pre->val) {
            diff = node->val - pre->val;
        }
        pre = node;
        traversal(node->right);
    }

Java:

    TreeNode pre = null;
    int diff = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        if (root == null) return 0;
        traversal(root);
        return diff;
    }
    void traversal(TreeNode root) {
        if (root == null) return;
        traversal(root.left);
        if (pre != null && diff > root.val - pre.val) {
            diff = root.val - pre.val;
        }
        pre = root;
        traversal(root.right);
    }

1.2 法2、迭代

1.2.1 版本Ⅰ、普通迭代

    int getMinimumDifference(TreeNode* root) {
        if (!root) return 0;
        int diff = INT_MAX;
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = nullptr;
        while (cur || !st.empty()) {
            // cur = st.top();
            if (cur) {
                // st.pop();
                st.push(cur);
                cur = cur->left;
            } else {
                // st.pop();
                cur = st.top();
                st.pop();
                if (pre && diff > cur->val - pre->val) {
                    diff = cur->val - pre->val;
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return diff;
    }

Java:

    public int getMinimumDifference(TreeNode root) {
        if (root == null) return 0;
        int diff = Integer.MAX_VALUE;
        TreeNode pre = null;    
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                if (pre != null && diff > cur.val - pre.val) {
                    diff = cur.val - pre.val;
                }
                pre = cur;
                cur = cur.right;
            }
        }
        return diff;
    }

1.2.2 版本Ⅱ、统一迭代

C++:

    int getMinimumDifference(TreeNode* root) {
        if (!root) return 0;
        int diff = INT_MAX;
        stack<TreeNode*> st;
        st.push(root);
        TreeNode* cur;
        TreeNode* pre = nullptr;
        while (!st.empty()) {
            cur = st.top();
            if (cur) {
                st.pop();
                if (cur->right) st.push(cur->right);
                st.push(cur);
                st.push(nullptr);
                if (cur->left) st.push(cur->left);
            } else {
                st.pop();
                cur = st.top();
                st.pop();
                if (pre && diff > cur->val - pre->val) {
                    diff = cur->val - pre->val;
                }
                pre = cur;
            }
        }
        return diff;
    }

Java:

    public int getMinimumDifference(TreeNode root) {
        if (root == null) return 0;
        int diff = Integer.MAX_VALUE;
        TreeNode pre = null;    
        TreeNode cur = null;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        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();
                if (pre != null && diff > cur.val - pre.val) {
                    diff = cur.val - pre.val;
                }
                pre = cur;
            }
        }
        return diff;
    }

1.3 总结

只要题目中说明了是二叉搜索树BST,就要灵活运用BST的特性。在递归或迭代过程中,记录前缀指针,以方便将其用于和当前指针比较,是一个相当好用的技巧~

2. T501:二叉搜索树中的众数

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

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

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值

  • 结点右子树中所含节点的值 大于等于 当前节点的值

  • 左子树和右子树都是二叉搜索树

提示:

  • 树中节点的数目在范围 [1, 104] 内

  • -105 <= Node.val <= 105

进阶:你可以不使用额外的空间吗?(假设由递归产生的隐式调用栈的开销不被计算在内)

S:遍历本身不是什么问题,主要是统计众数的逻辑要想清楚!

2.1 法1、递归

2.1.1 版本Ⅰ:for普通二叉树

简单地说,就是遍历二叉树同时统计各值(key)出现频率(value),按照频率排序,将最高的众数返回

C++:vector<pair<int, int>> 和 unordered_map<int, int> 应该就算使用了额外的空间

   vector<int> findMode(TreeNode* root) {
        vector<int> res;
        if (!root) return res;
        unordered_map<int, int> map;// key:元素,value:出现频率
        searchBST(root, map);

        vector<pair<int, int>> vec(map.begin(), map.end());
        sort(vec.begin(), vec.end(), cmp);// 降序排序
        res.push_back(vec[0].first);
        for (int i = 1; i < vec.size(); ++i) {
            if (vec[i].second == vec[0].second) {
                res.push_back(vec[i].first);
            } else break;// 如果一上来就频率小了,后面自然就不用看了
        }
        return res;
    }
private:
    void searchBST(TreeNode* node, unordered_map<int, int>& map) {
        if (!node) return;
        map[node->val]++;
        searchBST(node->left, map);
        searchBST(node->right, map);
    }
    // 指定降序的排序规则
    bool static cmp(const pair<int, int> a, const pair<int, int> b) {
        return a.second - b.second > 0;
    }

Java:

    public int[] findMode(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res.stream().mapToInt(Integer::intValue).toArray();
        Map<Integer, Integer> map = new HashMap<>();
        searchBST(root, map);
        List<Map.Entry<Integer, Integer>> mapList = map.entrySet().stream()
            .sorted((a, b) -> b.getValue() - a.getValue())//指定排序规则
            .collect(Collectors.toList());//Set无序,所以必须转为List
        res.add(mapList.get(0).getKey());
        // for (Integer entry : mapList) {//明显不行(无法跳过索引0)
        for (int i = 1; i < mapList.size(); ++i) {
            if (mapList.get(i).getValue() == mapList.get(0).getValue()) {
                res.add(mapList.get(i).getKey());
            } else break;
        }
        return res.stream().mapToInt(Integer::intValue).toArray();
    }
    private void searchBST(TreeNode node, Map<Integer, Integer> map) {
        if (node == null) return;
        map.put(node.val, map.getOrDefault(node.val, 0) + 1);
        searchBST(node.left, map);
        searchBST(node.right, map);
    }

2.1.2 版本Ⅱ:二叉搜索树

利用容器“动态清零”的思路,实现只用遍历二叉树一次,而且还不用额外的内存空间(Map)

C++:

public:
    vector<int> findMode(TreeNode* root) {
        if (!root) return res;
        searchBST(root);
        return res;
    }
private:
    vector<int> res;
    int count, maxCount;
    TreeNode* pre = nullptr;
    void searchBST(TreeNode* cur) {
        if (!cur) return;
        searchBST(cur->left);
        
        // 中
        if (!pre) {
            count = 1;
        } else if (pre->val == cur->val){
            ++count;
        } else {// 与前一个节点数值不同
            count = 1;
        }
        pre = cur;//别漏!

        //下面的不要并入上一串花括号!否则:[0]->error[null]
        if (count == maxCount) {//多众数
            res.push_back(cur->val);
        }
        if (count > maxCount) {
            maxCount = count;
            res.clear();//之前result里的元素都失效了
            res.push_back(cur->val);
        }
        searchBST(cur->right);
    }

Java:

    int count = 0, maxCount = 0;
    TreeNode pre = null;
    List<Integer> res;
    public int[] findMode(TreeNode root) {
        res = new ArrayList<>();
        // if (root == null) return res.stream().mapToInt(Integer::intValue).toArray();//不需要
        searchBST(root);
        return res.stream().mapToInt(Integer::intValue).toArray();
    }
    private void searchBST(TreeNode cur) {
        if (cur == null) return;
        searchBST(cur.left);
        if (pre == null) {
            // pre = cur;
            count = 1;
        } else {
            if (pre.val == cur.val) {
                ++count;
            } else {//别漏!
                count = 1;
            }
        }
        //下面的不要并入上一行的花括号!否则:[0]->error[null]
        if (count == maxCount) {
            res.add(cur.val);
        }
        if (count > maxCount) {
            maxCount = count;
            res.clear();
            res.add(cur.val);
        }
        pre = cur;
        searchBST(cur.right);
    }

2.2 法2、迭代

相关处理逻辑和递归一模一样,在迭代遍历的基础上加上就可以。
C++:
    vector<int> findMode(TreeNode* root) {
        vector<int> res;
        if (!root) return res;
        int count = 0, maxCount = 0;
        TreeNode* cur = root;
        TreeNode* pre = nullptr;
        stack<TreeNode*> st;
        while (cur || !st.empty()) {
            if (cur) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (!pre) {
                    count = 1;
                } else if (pre->val == cur->val) {
                    ++count;
                } else {
                    count = 1;
                }
                if (count == maxCount) {
                    res.push_back(cur->val);
                }
                if (count > maxCount) {
                    maxCount = count;
                    res.clear();
                    res.push_back(cur->val);
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return res;
    }

Java:

    public int[] findMode(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        if (root == null) return res.stream().mapToInt(Integer::intValue).toArray();
        int count = 0, maxCount = 0;
        TreeNode pre = null;
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur = cur.left;
            } else {
                cur = stack.pop();
                if (pre == null) {
                    count = 1;
                } else {
                    if (pre.val == cur.val) {
                        ++count;
                    } else {
                        count = 1;
                    }
                }
                if (count == maxCount) {
                    res.add(cur.val);
                }
                if (count > maxCount) {
                    maxCount = count;
                    res.clear();
                    res.add(cur.val);
                }
                pre = cur;
                cur = cur.right;
            }
        }
        return res.stream().mapToInt(Integer::intValue).toArray();
    }

3. T236:二叉树的最近公共祖先

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

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

提示:

  • 树中节点数目在范围 [2, 105] 内。

  • -109 <= Node.val <= 109

  • 所有 Node.val 互不相同 。

  • p != q

  • p 和 q 均存在于给定的二叉树中。

S:要想找到祖先,自然需要自底向上查找,后序遍历(左右中)就是天然的自下而上回溯过程。

判断逻辑是:如果递归遍历遇到q,就将q返回,遇到p就将p返回,那么 如果左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先

C++:

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left && right) return root;
        else if (left && !right) return left;
        else if (!left && right) return right;
        // else if (!left && !right) //这样写明的话->无默认返回值
        else return nullptr;
    }

最后那一串逻辑可以简化一下:

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (!root || root == p || root == q) return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left && right) return root;
        if (!left) return right;
        return left;
    }

Java:

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        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 left;
        // else if (left == null && right != null) return right;
        // else return null;

        //简化版
        if (left == null) return right;
        return left;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值