我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先

学习目标:

博主介绍: 27dCnc
专题 : 数据结构帮助小白快速入门

☆*: .。. o(≧▽≦)o .。.:*☆


学习时间:

  • 周一至周五晚上 7 点—晚上9点
  • 周六上午 9 点-上午 11 点
  • 周日下午 3 点-下午 6 点

主题: 二叉树

今日份打卡
在这里插入图片描述

  • 代码随想录-二叉树

学习内容:

  1. 二叉搜索树中的搜索
  2. 验证二叉搜索树
  3. 二叉搜索树的最小绝对差

内容详细 :

501. 二叉搜索树中的众数

题目考点 : 二叉搜索树 众数

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第1张图片

思路 :
将二叉树通过中序遍历转化为有序数组,然后在有序数组中统计重复元素

统计重复元素的方法

  1. arry[i] = arry[i - 1]
  2. unique()将重复元素移动到数组末尾

具体实现要分治

  1. 这个树都遍历了,用map统计频率
  2. 把统计的出来的出现频率(即map中的value)排个序
  3. 取前面高频的元素

分治之后具体代码实现

class Solution {
private:
    void searchBST(TreeNode* cur, unordered_map<int, int>& map) { // 前序遍历
        if (cur == NULL) return ;
        map[cur->val]++; // 统计元素频率
        searchBST(cur->left, map);
        searchBST(cur->right, map);
        return ;
    }
    bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
        return a.second > b.second;
    }
public:
    vector<int> findMode(TreeNode* root) {
        unordered_map<int, int> map; // key:元素,value:出现频率
        vector<int> result;
        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++) {
            // 取最高的放到result数组中
            if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
            else break;
        }
        return result;
    }
};

代码操作图示

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第2张图片

代码解图意

class Solution {
private:
    int maxCount = 0; // 最大频率
    int count = 0; // 统计频率
    TreeNode* pre = NULL;
    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中
            result.push_back(cur->val);
        }

        if (count > maxCount) { // 如果计数大于最大值频率
            maxCount = count;   // 更新最大频率
            result.clear();     // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
            result.push_back(cur->val);
        }

        searchBST(cur->right);      // 右
        return ;
    }

public:
    vector<int> findMode(TreeNode* root) {
        count = 0;
        maxCount = 0;
        pre = NULL; // 记录前一个节点
        result.clear();

        searchBST(root);
        return result;
    }
};

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

题目考点 : 二叉树结点关系 二叉树 基础条件判断

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第3张图片

思路

首先想的是要是能自底向上查找,通过回溯查找,二叉树回溯的过程就是从低到上

情况一
首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。 即情况一:

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第4张图片
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。

情况二:
二叉树节点数值是不重复的,而且一定存在 q 和 p。但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点 q ( p ) q(p) q(p)。 情况二:

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第5张图片

递归三部曲:

  1. 确定递归函数返回值以及参数
  2. 确定终止条件
  3. 确定单层递归逻辑

在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)。

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第6张图片

如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解

如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。

单层递归逻辑如下

if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else  { //  (left == NULL && right == NULL)
    return NULL;
}

所以整体代码如下

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(p == root||q == root  ||root== NULL) return root;
        TreeNode*N = lowestCommonAncestor(root->left,p,q);
        TreeNode*O = lowestCommonAncestor(root->right,p,q);
        if(N != NULL && O != NULL) return root;//不一定是左边是p右边是q
        if(N == NULL&&O != NULL) return O;//为什么返回左子树因为是回溯左右子树的根节点就是
        if(N != NULL&& O == NULL) return N;//主树的左右子节点所以要返回
        return NULL;
    }
};

235. 二叉搜索树的最近公共祖先

题目考点 : 二叉搜索树 公共祖先

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第7张图片

上题如果会那么这题明显是送分

图解

如图,我们从根节点搜索,第一次遇到 cur节点是数值在[q, p]区间中,即 节点5,此时可以说明 q 和 p 一定分别存在于 节点 5的左子树,和右子树中。
我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第8张图片

此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为p的祖先, 如果从节点5继续向右遍历则错过成为q的祖先。

所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[q, p]区间中,那么cur就是 q和p的最近公共祖先。

理解这一点,本题就很好解了。

而递归遍历顺序,本题就不涉及到 前中后序了(这里没有中节点的处理逻辑,遍历顺序无所谓了)。

如图所示:p为节点6,q为节点9
我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第9张图片
可以看出直接按照指定的方向,就可以找到节点8,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!

详细代码

class Solution {
private:
    TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
        if (cur == NULL) return cur;
                                                        // 中
        if (cur->val > p->val && cur->val > q->val) {   // 左
            TreeNode* left = traversal(cur->left, p, q);
            if (left != NULL) {
                return left;
            }
        }

        if (cur->val < p->val && cur->val < q->val) {   // 右
            TreeNode* right = traversal(cur->right, p, q);
            if (right != NULL) {
                return right;
            }
        }
        return cur;
    }
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return traversal(root, p, q);
    }
};

改进后代码

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root->val > p->val && root->val > q->val) {
            return lowestCommonAncestor(root->left, p, q);
        } else if (root->val < p->val && root->val < q->val) {
            return lowestCommonAncestor(root->right, p, q);
        } else return root;
    }
};

提示: 上一题的代码这题也可以通过!


学习产出:

  • 技术笔记 2 遍
  • CSDN 技术博客 3 篇
  • 习的 vlog 视频 1 个

我在代码随想录|写代码Day21之二叉树-501. 二叉搜索树中的众数,236. 二叉树的最近公共祖先,235. 二叉搜索树的最近公共祖先_第10张图片

如果此文对你有帮助的话,欢迎关注、点赞、⭐收藏、✍️评论,支持一下博主~

你可能感兴趣的:(数据结构与算法,C/C++语言刷题,c++,笔记,数据结构)