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

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

题目链接:二叉搜索树的最小绝对差

题目描述:给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。

解题思想:

这道题主要利用了二叉搜索树的中序遍历是一个有序序列,这个题就变为了一个求有序序列的最小绝对差问题。

递归法:

主要使用了双指针
在这里插入图片描述

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return MinDiff;
    }

private:
    TreeNode* pre;
    int MinDiff = INT32_MAX;
    void traversal(TreeNode* cur) {
        if (cur == NULL)
            return;
        traversal(cur->left);
        if (pre != NULL) {
            int diff = cur->val - pre->val;
            if (diff < MinDiff)
                MinDiff = diff;
        }
        pre = cur;
        traversal(cur->right);
    }
};

迭代法:

主要利用迭代法中序遍历模版,加上双指针。

class Solution {
public:
    int getMinimumDifference(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        int MinDiff = INT32_MAX;
        while (cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (pre != NULL) {
                    int diff = cur->val - pre->val;
                    MinDiff = min(MinDiff, diff);
                }
                pre = cur;
                cur = cur->right;
            }
        }
        return MinDiff;
    }
};

501.二叉搜索树中的众数

题目链接:二叉搜索树中的众数

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

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

假定 BST 满足如下定义:

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

递归法:

当作普通二叉树来处理:

  1. 这个树都遍历了,用map统计频率

  2. 把统计的出来的出现频率(即map中的value)排个序

    有的同学可能可以想直接对map中的value排序,还真做不到,C++中如果使用std::map或者std::multimap可以对key排序,但不能对value排序。

  3. 取前面高频的元素

class Solution {
public:
    vector<int> findMode(TreeNode* root) {
        vector<int> result;
        unordered_map<int, int> map;
        if (root == NULL)
            return result;
        searchBST(root, map);
        vector<pair<int, int>> vec(map.begin(), map.end());
        sort(vec.begin(), vec.end(), cmp);
        result.push_back(vec[0].first);
        for (int i = 1; i < vec.size(); i++) {
            if (vec[i].second == vec[0].second)
                result.push_back(vec[i].first);
            else
                break;
        }
        return result;
    }

private:
    void searchBST(TreeNode* node, unordered_map<int, int> &map) {
        if (node == NULL) {
            return;
        }
        searchBST(node->left, map);
        map[node->val]++;
        searchBST(node->right, map);
    }
    bool static cmp(const pair<int, int>& a, const pair<int, int>& b) {
        return a.second > b.second;
    }
};

利用搜索二叉树的性质:中序遍布为单调递增序列。

!https://code-thinking-1253855093.file.myqcloud.com/pics/20210204152758889.png

遍历有序数组的元素出现频率,从头遍历,那么一定是相邻两个元素作比较,然后就把出现频率最高的元素输出就可以了。这里我们依然利用双指针的方法,分别指向cur和pre。

class Solution {
public:
    vector<int> findMode(TreeNode* root) {
        searchBST(root);
        return result;
    }

private:
    TreeNode* pre = NULL;
    int count = 0;
    int maxCount = 0;
    vector<int> result;
    void searchBST(TreeNode* cur) {
        if (cur == NULL)
            return;
        searchBST(cur->left);

        if (pre == NULL) {
            count = 1;
        } else if (pre->val == cur->val)
            count++;
        else
            count = 1;
        pre = cur;

        if (count == maxCount) {
            result.push_back(cur->val);
        }

        if (count > maxCount) {
            maxCount = count;
            result.clear();
            result.push_back(cur->val);
        }

        searchBST(cur->right);
    }
};

迭代法:

只要把中序遍历转成迭代,中间节点的处理逻辑完全一样

class Solution {
public:
    vector<int> findMode(TreeNode* root) {
        stack<TreeNode*> st;
        TreeNode* cur = root;
        TreeNode* pre = NULL;
        int count = 1, maxCount = 0;
        vector<int> result;

        while (cur != NULL || !st.empty()) {
            if (cur != NULL) {
                st.push(cur);
                cur = cur->left;
            } else {
                cur = st.top();
                st.pop();
                if (pre == NULL)
                    count = 1;
                else if (pre->val == cur->val)
                    count++;
                else
                    count = 1;
                pre = cur;

                if (count == maxCount) {
                    result.push_back(cur->val);
                }

                if (count > maxCount) {
                    maxCount = count;
                    result.clear();
                    result.push_back(cur->val);
                }

                cur = cur->right;
            }
        }
        return result;
    }
};

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

题目链接:二叉树的最近公共祖先

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

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

解题思路:

后序遍历,通过左右子树是否包含p和q的公共祖先来将公共祖先向上传递。
在这里插入图片描述

那么我给大家归纳如下三点

  1. 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。
  2. 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
  3. 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return traversal(root, p, q);
    }
    TreeNode* traversal(TreeNode* node, TreeNode* p, TreeNode* q) {
        if (node == NULL)
            return NULL;
        if (node == p || node == q)
            return node;
        TreeNode* left = traversal(node->left, p, q);
        TreeNode* right = traversal(node->right, p, q);
        TreeNode* result;
        if (left != NULL && right != NULL)
            result = node;
        else if (left != NULL && right == NULL)
            result = left;
        else if (left == NULL && right != NULL)
            result = right;
        else
            result = NULL;
        return result;
    }
};
  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值