代码随想录算法训练营第二十一天 | 二叉树众数、公共祖先

目录

力扣题目

力扣题目记录

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

501.二叉搜索树中的众数

普通二叉树

搜索二叉树

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

总结

总结


力扣题目

用时:2h

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

2、501.二叉搜索树中的众数

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


力扣题目记录

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

        第一种做法,将搜索树变为有序数组,然后两两做差比较

class Solution {
private:
vector<int> vec;
void traversal(TreeNode* root) {
    if (root == NULL) return;
    traversal(root->left);
    vec.push_back(root->val); // 将二叉搜索树转换为有序数组
    traversal(root->right);
}
public:
    int getMinimumDifference(TreeNode* root) {
        vec.clear();
        traversal(root);
        if (vec.size() < 2) return 0;
        int result = INT_MAX;
        for (int i = 1; i < vec.size(); i++) { // 统计有序数组的最小差值
            result = min(result, vec[i] - vec[i-1]);
        }
        return result;
    }
};

        第二种做法,边遍历边比较,这样就需要使用双指针来指向前一个节点的位置

class Solution {
private:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
    if (cur == NULL) return;
    traversal(cur->left);   // 左
    if (pre != NULL){       // 中
        result = min(result, cur->val - pre->val);
    }
    pre = cur; // 记录前一个
    traversal(cur->right);  // 右
}
public:
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

501.二叉搜索树中的众数

普通二叉树

具体步骤如下:

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

至于用前中后序哪种遍历也不重要,因为就是要全遍历一遍,怎么个遍历法都行,层序遍历都没毛病!

这里采用前序遍历,代码如下:

// map<int, int> key:元素,value:出现频率
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 ;
}

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

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

所以要把map转化数组即vector,再进行排序,当然vector里面放的也是pair<int, int>类型的数据,第一个int为元素,第二个int为出现频率。

代码如下:

bool static cmp (const pair<int, int>& a, const pair<int, int>& b) {
    return a.second > b.second; // 按照频率从大到小排序
}

vector<pair<int, int>> vec(map.begin(), map.end());
sort(vec.begin(), vec.end(), cmp); // 给频率排个序

        3、取前面高频的元素

此时数组vector中已经是存放着按照频率排好序的pair,那么把前面高频的元素取出来就可以了。

代码如下:

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;

整体C++代码如下:

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;
    }
};

搜索二叉树

  1. 利用双指针,其中一个指向前一个节点,来比较这两个值,如果一样,则数量加一
  2. 所以需要一个count来记录数量、同时需要一个maxCount来做比较
  3. 当count=maxCount时,就将对应元素添加到数组中
  4. 这道题的精髓在于不断更新maxCount,如果count>maxCount时,清空数组,更新maxCount
  5. 这样的话只需要遍历一遍树,不然需要先遍历一遍找到众数,再遍历第二遍记录元素
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. 二叉树的最近公共祖先

  1. 从下向上遍历,即后序遍历
  2. 返回值记录最近节点,要不记录NULL,要不记录最近公共祖先,并不断向上传递

那么寻找最小公共祖先,完整流程图如下:

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

从图中,大家可以看到,我们是如何回溯遍历整棵二叉树,将结果返回给头结点的!

整体代码如下:

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == q || root == p || 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;

        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 (root == q || root == p || 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;
        if (left == NULL) return right;
        return left;
    }
};

总结

这道题目刷过的同学未必真正了解这里面回溯的过程,以及结果是如何一层一层传上去的。

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

  1. 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。

  2. 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。

  3. 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。


总结

        最后一道题有难度的,感觉现在这种记录方式更适合我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/258792.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

02 ModBus TCP

目录 一、ModBus TCP 一帧数据格式 二、0x01 读线圈状态 三、0x03读保持寄存器 四、0x05写单个线圈 五、0x06 写单个寄存器 六、0x0f写多个线圈 七、0x10&#xff1a;写多个保持寄存器 八、通信过程 九、不同modbus通信模式的应用场景 一、ModBus TCP 一帧数据格式 其…

队列(C语言版)

一.队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有 先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为 队尾 出队列&#xff1a;进行删除操作的一端称为…

【大数据面试】Flink面试题附答案

目录 ✅Flink介绍、特点、应用场景 ✅Flink与Spark Streaming的区别 ✅Flink有哪些部署模式 ✅Flink架构 ✅怎么设置并行度&#xff1f; ✅什么是算子链&#xff1f; ✅什么是任务槽&#xff08;Task Slots&#xff09;&#xff1f; ✅任务槽和并行度的关系 ✅Flink作…

自动化测试入门 —— 自动化测试概论

整篇论述总的来讲会很长&#xff0c;从自动化的思维、模型、工具&#xff0c;到各层次的自动化测试技术、测试框架、测试平台&#xff0c;包括面向未来的自动化技术都将涉及&#xff0c;因此打算拆成几个部分去写。此外&#xff0c;由于涉及的范围比较广泛&#xff0c;部分内容…

C++内存布局(二)

在《C内存布局(一)》 中&#xff0c;我们介绍了C内存布局的基本知识&#xff0c;本篇我们仍着重探讨C类的内存布局&#xff0c;尤其是 多重继承、钻石继承&#xff08;菱形继承&#xff09;场景下的虚函数表的情况。 一、多重继承 1.1 示例 class A { public:virtual void d…

LabVIEW软件模拟氢燃料电池在车辆中的应用

LabVIEW软件模拟氢燃料电池在车辆中的应用 在追求可持续能源的时代&#xff0c;氢燃料电池在绿色经济中扮演着关键角色。本研究通过LabVIEW软件模拟和评估了氢燃料电池在车辆应用中的性能和效率。LabVIEW作为一个强大的模拟工具&#xff0c;能够动态模拟氢燃料电池系统在不同条…

js键盘事件keydown事件,防止重复触发,组合键的配合使用

js键盘事件keydown事件&#xff0c;防止重复触发 键盘事件类型主要有三种&#xff1a; keydown 、keypress(不建议使用&#xff0c;部分浏览器已放弃)和 keyup 。 添加普通键盘keydown事件 // 监听键盘按下事件document.addEventListener(keydown, function(event) {// 输出按…

3 python基本语法 - Dict 字典

Python 中字典&#xff08;dict&#xff09;是一种无序的、可变的序列&#xff0c;它的元素以“键值对&#xff08;key-value&#xff09;”的形式存储。相对地&#xff0c;列表&#xff08;list&#xff09;和元组&#xff08;tuple&#xff09;都是有序的序列&#xff0c;它们…

23、Web攻防——Python考点CTF与CMS-SSTI模板注入PYC反编译

文章目录 一、PYC文件二、SSTI 一、PYC文件 pyc文件&#xff1a;python文件编译后生成的字节码文件&#xff08;byte code&#xff09;&#xff0c;pyc文件经过python解释器最终会生成机器码运行。因此pyc文件是可以跨平台部署的&#xff0c;类似java的.class文件&#xff0c;…

Vue-图片懒加载

实现图片懒加载可以使用vue-lazyload插件 npm 链接&#xff1a;vue-lazyload - npm (npmjs.com) 使用方法&#xff1a; 1. 安装vue-lazyload npm i vue-lazyload npm i vue-lazyload1.3.3 // 如果是vue2就需要安装1.3.3版本 2. 引入vue-lazyload并使用 可以在使用该插…

软件企业在什么情况下需要找第三方软件测试机构?如何收费?

近年来&#xff0c;随着软件行业的迅猛发展&#xff0c;软件企业对软件测试的需求也越来越大。为了保证软件的质量和稳定性&#xff0c;许多企业选择寻找第三方软件测试机构来进行软件测试。第三方软件测试机构是独立于软件开发企业的专业机构&#xff0c;主要从事软件测试和质…

每日一题 2828. 判别首字母缩略词(简单)

简单题&#xff0c;就不多写了 class Solution:def isAcronym(self, words: List[str], s: str) -> bool:if len(words) ! len(s):return Falsefor i in range(len(words)):if words[i][0] ! s[i]:return Falsereturn True

栈(stack)

栈&#xff08;stack&#xff09;是一种用于存储数据的简单数据结构&#xff0c;与链表和顺序表很相似&#xff0c;最大的区别在于数据的存取操作。栈的插入和删除操作只允许在一端执行&#xff0c;因此把允许操作的一端称为栈顶&#xff0c;不允许操作的称为栈底。插入元素称为…

轻度听力损失的儿童需要早期干预吗?

一些宝宝在做听力筛查时总是不通过&#xff0c;进一步听力诊断发现宝宝有轻度的听力损失&#xff0c;刚知道这个消息时&#xff0c;家长可担心了&#xff0c;总想着宝宝是不是听不到啊&#xff1f;但是一段时间后&#xff0c;有些家长又会忽略宝宝的听力问题&#xff0c;因为部…

7款创意性前端源码特效资源分享(附在线预览效果)

分享7款非常不错炫酷的前端特效源码 其中包含css动画特效、js原生特效、svg特效等 下面我会给出特效样式图或演示效果图 但你也可以点击在线预览查看源码的最终展示效果及下载源码资源 CSS绘制iPhone 14带动态岛 纯CSS绘制iPhone 14带动态岛模型 运行初始化时还附带出场动画 …

防冻水表是什么?

防冻水表是一种特殊类型的水表&#xff0c;它主要用于防止水管和设备在寒冷的冬季中受到冻结和损坏的情况。在冷却系统中使用防冻水表可以有效地监测和控制液体的温度&#xff0c;从而保护系统的正常运行。 防冻水表通常由温度传感器、控制器和显示器组成。温度传感器负责测量液…

阿里云k8s集群搭建

文章目录 一、安装前准备1.环境2.k8s集群规划 二、k8s 安装1. centos基础设置2. docker 安装3. k8s安装3.1 添加阿里云 yum 源3.2 安装 kubeadm、kubelet、kubectl3.3 部署 Kubernetes Master3.4 加入 Kubernetes Node3.5 部署 CNI 网络插件3.6 测试 kubernetes 集群 一、安装前…

鸿蒙Harmony4.0开发-ArkTS基础知识运用

概念 1.渲染控制语法&#xff1a; 条件渲染&#xff1a;使用if/else进行条件渲染。 Column() {if (this.count > 0) {Text(count is positive)} }循环渲染&#xff1a;开发框架提供循环渲染&#xff08;ForEach组件&#xff09;来迭代数组&#xff0c;并为每个数组项创建…

abpvnext框架的项目部署到linux arm64版的docker中

参考&#xff1a; windows10下安装的docker 导出镜像到另一个电脑_docker镜像拷贝另一台机器的镜像-CSDN博客 前提条件&#xff1a; 1、vs2022&#xff0c;我的电脑本机安装有windows版docker desktop 。 2、linux中已经安装好docker&#xff0c;安装了sftp。这部分可以自行…