LeetCode刷题笔记【15】:二叉树专题-7( 二叉搜索树的最小绝对差、二叉搜索树中的众数、二叉树的最近公共祖先)

前置知识

参考前文

参考文章:
LeetCode刷题笔记【9】:二叉树专题-1(分别用递归遍历、迭代遍历、标记遍历实现前、中、后序遍历)
LeetCode刷题笔记【10】:二叉树专题-2(二叉树的层序遍历、翻转二叉树、对称二叉树)
LeetCode刷题笔记【10.5】:二叉树专题-2.5(二叉树的层序遍历 - 10道题)
LeetCode刷题笔记【11】:二叉树专题-3(二叉树的最大深度、二叉树的最小深度、完全二叉树的节点个数)
LeetCode刷题笔记【12】:二叉树专题-4(平衡二叉树、二叉树的所有路径、左叶子之和)
LeetCode刷题笔记【13】:二叉树专题-5(找树左下角的值 、路径总和、从前序与中序遍历序列构造二叉树、从中序与后序遍历序列构造二叉树)
LeetCode刷题笔记【14】:二叉树专题-6(最大二叉树 、合并二叉树 、二叉搜索树中的搜索 、验证二叉搜索树)

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

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/minimum-absolute-difference-in-bst/description/

遍历生成数组, sort(通用, 不针对二叉搜索树)

思路: 遍历一遍, 生成数组, sort数组, 遍历求最小差

class Solution {
private:
    vector<int> marker;
public:
    void traversal(TreeNode* root){
        if(root==nullptr)
            return;
        marker.push_back(root->val);
        traversal(root->left);
        traversal(root->right);
        return;
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        sort(marker.begin(), marker.end());
        int ans=INT_MAX;
        for(int i=0; i<marker.size()-1; ++i){
            ans = min(ans, marker[i+1]-marker[i]);
        }
        return ans;
    }
};

利用二叉搜索树性质, 中序遍历生成数组

以上方法虽然可行, 但是没有使用二叉搜索树的性质;
因为是二叉搜索树, 所以只要以中序遍历, 形成的序列就是递增的

class Solution {
private:
    vector<int> marker;
public:
    void traversal(TreeNode* root){
        if(root==nullptr)
            return;
        traversal(root->left);
        marker.push_back(root->val);
        traversal(root->right);
        return;
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        int ans=INT_MAX;
        for(int i=0; i<marker.size()-1; ++i){
            ans = min(ans, marker[i+1]-marker[i]);
        }
        return ans;
    }
};

利用二叉搜索树性质, 且不用数组额外空间

其实不用在过程中维护marker数组, 只需要用int pre来记录在序列中的前一个节点的val;
具体的操作是最开始让pre=-1, 之后每次都pre=-1时让pre=root->val;
注意过程中传递的是preans的引用;

class Solution {
public:
    void traversal(TreeNode* root, int& ans, int& pre){
        if(root==nullptr)
            return;
        traversal(root->left, ans, pre);
        if(pre == -1){
            pre = root->val;
        }else{
            ans = min(ans, root->val-pre);
            pre = root->val;
        }
        traversal(root->right, ans, pre);
    }
    int getMinimumDifference(TreeNode* root) {
        int ans=INT_MAX, pre=-1;
        traversal(root, ans, pre);
        return ans;
    }
};

501.二叉搜索树中的众数

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/find-mode-in-binary-search-tree/description/

用unordered_map统计出现频率

unordered_map<int, int>记录每个数出现的次数, 看最大值

class Solution {
private:
    unordered_map<int,int> map;
public:
    void dfs(TreeNode* root){
        if(root==nullptr)
            return;
        map[root->val]++;
        dfs(root->left);
        dfs(root->right);
        return;
    }
    vector<int> findMode(TreeNode* root) {
        dfs(root);
        int maxNum=INT_MIN;
        for(auto& pair : map){
            maxNum = max(maxNum, pair.second);
        }
        vector<int> ans;
        for(auto& pair : map){
            if(pair.second==maxNum){
                ans.push_back(pair.first);
            }
        }
        return ans;
    }
};

利用二叉搜索树性质进行遍历

那么能不能和<530. 二叉搜索树的最小绝对差>中一样
使用precur进行遍历呢?
过程中检查cur->valpre->val是否一样, 如果一样counter++, 不一样counter=1
且过程中维护vector<int> ansint maxNum
counter==maxNum的时候, 在ans中加入cur->val
counter>maxNum的时候, 清空ans, 并加入cur->val, 更新maxNum
及时用cur替换pre

class Solution {
private:
    vector<int> ans;
    int maxNum=0, counter=0;
    TreeNode* pre=nullptr;
    void traversal(TreeNode* cur){
        if(cur==nullptr)
            return;
        traversal(cur->left);
        if(pre==nullptr){
            counter = 1;
        }else if(pre->val == cur->val){
            counter++;
        }else if(pre->val != cur->val){
            counter = 1;
        }
        if(counter == maxNum){
            ans.push_back(cur->val);
        }else if(counter > maxNum){
            ans.clear();
            ans.push_back(cur->val);
            maxNum = counter;
        }
        pre = cur;
        traversal(cur->right);
        return;
    }
public:
    vector<int> findMode(TreeNode* root) {
        traversal(root);
        return ans;
    }
};

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

题目描述

在这里插入图片描述

LeetCode链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

分别搜索两个节点, 生成搜索序列, 比较搜索序列

思路①: 分别查找两个节点, 生成两个查找队列, 然后检查两个队列, 从哪个节点开始不一样了, 那个节点的上一个节点就是"最近公共祖先"

class Solution {
private:
    vector<TreeNode*> path;
public:
    bool dfs(TreeNode* root, TreeNode* target, vector<TreeNode*>& path){
        if(root==NULL)
            return false;
        if(root==target){
            path.push_back(root);
            return true;
        }
        path.push_back(root);
        if(dfs(root->left, target, path) || dfs(root->right, target, path)){
            return true;
        }
        path.pop_back();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector<TreeNode*> pathP, pathQ;
        dfs(root, p, pathP);
        dfs(root, q, pathQ);

        int i;
        for(i=0; i<pathP.size()&&i<pathQ.size(); ++i){
            if(pathP[i]!=pathQ[i]){
                break;
            }
        }
        return pathP[i-1];
    }
};

回溯法

在这里插入图片描述
如上图所示, 参考后序遍历的方式对整棵树进行递归的后序遍历.
每一次递归返回NULL或者相应的查找成功节点.

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL || p==root || q==root)
            return root;
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if(left!=NULL && right!=NULL)
            return root;
        if(left==NULL && right!=NULL)
            return right;
        if(left!=NULL && right==NULL)
            return left;
        // if(left==NULL && right==NULL)
        return NULL;
    }
};

总结

对于二叉搜索树性质的使用: 中序遍历即可获得递增序列
回溯法的顺序: 参考后序遍历可类比

对于二叉搜索树的性质, 以及回溯法, 还不是很熟练.
但是回溯法的使用相关题目似乎还挺常见的, 需要加强练习(后续似乎有专门的回溯专题)

本文参考:
二叉搜索树的最小绝对差
二叉搜索树中的众数
二叉树的最近公共祖先

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值