代码随想录算法训练营第18天 | LeetCode530.二叉搜索树的最小绝对差、LeetCode501.二叉搜索树中的众数、LeetCode236.二叉树的最近公共祖先

目录

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

1. 递归法

1.1 转换法 

1.2 双指针法(记录前结点法)

2. 迭代法

LeetCode501.二叉搜索树中的众数

1. 递归法

1.1 普通二叉树

1.2 二叉搜索树

2. 迭代法

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


 

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

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

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

思路:之前讲过类似的二叉搜索树的题目,解决方法的关键在于抓住其有序的特性,这里介绍两种方法,递归法迭代法

1. 递归法

1.1 转换法 

转换法也就是将二叉搜索树利用中序遍历转换成有序数组,对数组元素进行操作,这样就简单了。

    vector<int> result;
    void traversal(TreeNode* cur){
        if(cur == NULL) return;
        traversal(cur -> left);//左
        result.push_back(cur -> val);//中
        traversal(cur -> right); //右
    }
    int getMinimumDifference(TreeNode* root) {
        result.clear();
        traversal(root);
        if(result.size() < 2) return 0;
        int min_val = INT_MAX;
        for(int i = 0; i < result.size() - 1; i ++){
            min_val = min(min_val, result[i + 1] - result[i]);//记录最小值
        }
        return min_val;
    }

1.2 双指针法(记录前结点法)

同样是采用了递归,不过这里没有借助辅助数组,而是采用一个pre结点指向遍历结点的前一个结点,然后在中序遍历过程中不断更新最小值。

    int min_val = INT_MAX;
    TreeNode* pre = NULL;//准备记录前一个结点
    void traversal(TreeNode* cur){
        if(cur == NULL) return;
        traversal(cur -> left);//左
        if(pre != NULL){
            min_val = min(min_val, cur -> val - pre -> val);//中
        }
        pre = cur; //记录前一个结点
        traversal(cur -> right);//右
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return min_val;
    }

2. 迭代法

迭代法这里采用了中序遍历的迭代方法,同样采用了pre记录前一个结点,这样可以和当前结点作比较。

int getMinimumDifference(TreeNode* root) {
        stack<TreeNode*> st;
        int min_val = INT_MAX;
        TreeNode* pre = NULL;
        //考虑到root可能为空或者只有一个根节点
        if(root == NULL || (root -> left == NULL && root -> right == NULL)) return 0; 
        TreeNode* cur = root;
        while(cur != NULL|| !st.empty()){
            if(cur != NULL){
                st.push(cur);
                cur = cur -> left; //左
            }else{
                cur = st.top(); //中
                st.pop();
                if(pre != NULL){
                    //记录最小值
                    min_val = min(min_val, cur -> val - pre -> val);
                }
                pre = cur;
                cur = cur -> right;//右
            }
        }
        return min_val;
    }

LeetCode501.二叉搜索树中的众数

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

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

假定 BST 满足如下定义:

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

思路:这里的在树中找众数,同样有递归法迭代法两种。递归中我列举了一般的二叉树如何查找以及二叉搜索树如何查找。

1. 递归法

1.1 普通二叉树

大体来说就是先遍历二叉树的所有元素,将元素出现频率放入map中,然后通过一次循环找到最大频率,然后再一次循环找到最大频率对应的元素。

需要注意的是,不同结点值最大频率可能相等,还有就是遍历的顺序选择前、中、后序,甚至层序遍历都可以,因为是需要遍历所有元素,不在乎遍历的顺序。

    unordered_map<int, int> mp;//统计各元素出现频率
    void traversal(TreeNode* cur){
        if(cur == NULL) return;
        traversal(cur -> left);//左
        mp[cur -> val] ++;//中
        traversal(cur -> right);//右
    }
    vector<int> findMode(TreeNode* root) {
        vector<int> result;
        int max_val = INT_MIN;
        traversal(root);
        for(auto iter = mp.begin(); iter != mp.end(); iter ++){
            if(iter -> second > max_val) max_val = iter -> second;//统计出最大频率
        }
        for(auto iter: mp){
            if(iter.second == max_val) result.push_back(iter.first);//统计出最大频率元素
        }
        return result;
    }

1.2 二叉搜索树

对于二叉搜索树的话,因为它是有序的,所以我们可以不使用额外的map,而是设置一个pre结点,记录前一个结点的情况,每次遍历的时候都会对当前结点的情况做出判断,遍历的是第一个结点时,会记录count=1,显然这会更新maxCount的值,然后加入result中,然后再遍历后面的结点,如果值不相等,那任然是对当前结点情况做出判断,count=1;而如果相等,count就会增加,相应maxCount也会修改,同时会清空之前result里面的失效结点值,加入新的结点值。当然,每次操作完一个结点都需要使pre指向它,这样后面遍历其他结点时,它能够作为前一个结点进行判断。

如此重复以上操作,最后通过一次遍历便能顺利返回result。

    int maxCount = 0; //记录最大频率
    int count = 0; //记录出现次数
    vector<int> result; 
    TreeNode* pre = NULL;//记录前一个结点
    void traversal(TreeNode* cur){
        if(cur == NULL) return;
        traversal(cur -> left);//左
        
        if(pre == NULL || cur -> val != pre -> val){
        //当遍历的是第一个结点或者当前结点值和前一个结点值不相等时
            count = 1;
        }else if(pre -> val == cur -> val){
        //当当前结点值与前一个结点值相等时
            count ++;
        }
        pre = cur;//指向前一个结点

        if(count == maxCount){
        //count和maxCount相等时,将其加入result;
            result.push_back(cur -> val);
        }
        if(count > maxCount){
        //这里主要是为了处理当前maxCount不是最大值时的情况
        //将maxCount重新赋值后,需要清空之前的result,因为里面的元素已经失效了
        //再将新的值放入
            maxCount = count;
            result.clear();//清空result中的失效元素
            result.push_back(cur -> val);
        }

        traversal(cur -> right);//右
    }
    vector<int> findMode(TreeNode* root) {
       traversal(root);
        return result;
    }

2. 迭代法

迭代法的话还是采用了中序遍历的迭代方法,在对于中间结点处理的逻辑与上面二叉搜索树中的递归方法处理中间结点的逻辑一样。

    vector<int> findMode(TreeNode* root) {
        vector<int> result;
        stack<TreeNode*> st;
        int maxCount = 0;
        int count = 0;
        TreeNode* pre = NULL;
        TreeNode* cur = root;
        while(cur != NULL || !st.empty()){
            if(cur != NULL){
                st.push(cur);
                cur = cur -> left;//左
            }else{
                cur = st.top();//中
                st.pop();

                if(pre == NULL || cur -> val != pre -> val){
                    count = 1;
                }else if(cur -> val == pre -> val){
                    count ++;
                }
                pre = cur; //指向前一个结点
                if(count == maxCount){
                    result.push_back(cur -> val);
                }else if(count > maxCount){
                    maxCount = count;
                    result.clear();
                    result.push_back(cur -> val);
                }
                cur = cur -> right; //右
            }
        }
        return result;
    }

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

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

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

思路:这里需要注意,一个结点本身也可以作为它自己的祖先,于是,当我们遍历遇到了p或者q时,可以直接返回它;另外,这道题是需要将整棵树进行遍历,自底向上,采用后序遍历,因为后面需要对左右子树的结果进行逻辑判断处理,后序遍历本身就是左右中的顺序,是天然的回溯遍历,能够在找到后直接返回,在逻辑判断的时候如果左右都不为空,那么说明左右找到了,那么该左右的中间结点即为该左右的最近公共祖先,如果一方为空,那就返回另一方不为空的,因为会存在这样的情况,比如该树左子树没有找到p、q,但是右子树存在,需要返回右子树的结果,当然,如果右子树没有,左子树存在也是这个道理。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == p || root == q || root == NULL) return root;
        TreeNode* left = lowestCommonAncestor(root -> left, p, q);//统计左子树,左
        TreeNode* right = lowestCommonAncestor(root -> right, p, q);//统计右子树,右
        if(left != NULL && right != NULL) return root;//左右子树返回不空,说明找到了,返回root,中
        if(left != NULL && right == NULL) return left;//这里如果一方为空,一方不为空,那就返回不为空的结果
        else if(left == NULL && right != NULL) return right;
        else return NULL;//两方都为空了,直接返回NULL,说明没有找到公共祖先
    }

 下面的代码是对上面代码的精化。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == p || root == q || root == NULL) return root;
        TreeNode* left = lowestCommonAncestor(root -> left, p, q);//统计左子树,左
        TreeNode* right = lowestCommonAncestor(root -> right, p, q);//统计右子树,右
        if(left != NULL && right != NULL) return root;//左右子树返回不空,说明找到了,返回root,中
        if(left == NULL) return right;//这里如果一方为空,那就返回另一方的结果
        return left;
    }

感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。

如果有什么问题欢迎评论区讨论!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值