代码随想录刷题题Day17

news2024/12/30 1:45:09

刷题的第十七天,希望自己能够不断坚持下去,迎来蜕变。😀😀😀
刷题语言:C++
Day17 任务
● 530.二叉搜索树的最小绝对差
● 501.二叉搜索树中的众数
● 236. 二叉树的最近公共祖先

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

530.二叉搜索树的最小绝对差
在这里插入图片描述

利用二叉搜索树的特性:二叉搜索树是有序的

遇到在二叉搜索树上求最值,求差值,就把它想象成在一个有序数组上求最值,求差值

递归法
二叉搜索树采用中序遍历,就是一个有序数组
思路1:

把二叉搜索树转换成有序数组,然后遍历一遍数组,就统计出来最小差值

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

思路2:

在二叉搜素树中序遍历的过程中就可以直接计算
用一个pre节点记录cur节点的前一个节点

在这里插入图片描述

class Solution {
public:
    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);// 右
    }
    int getMinimumDifference(TreeNode* root) {
        traversal(root);
        return result;
    }
};

2 二叉搜索树中的众数

501.二叉搜索树中的众数
在这里插入图片描述
递归法
二叉搜索树:既然是搜索树,它中序遍历就是有序的
在这里插入图片描述
中序遍历代码:

void traversal(TreeNode* cur) {
	if (cur == NULL) return;
	traversal(cur->left); // 左
	处理节点               // 中
	traversal(cur->right);// 右
	return;
}

处理节点逻辑:

if (pre == NULL) count = 1;// 第一个节点
else if (pre->val == cur->val) count++;// 与前一个节点数值相同
else count = 1;// 与前一个节点数值不同
pre = cur;// 更新上一个节点

只需要遍历一次就可以找到所有的众数

频率count 等于 maxCount(最大频率),把这个元素加入到结果集中(以下代码为result数组)
频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效

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

C++:

class Solution {
public:
    int count = 0;   // 统计频率
    int maxCount = 0;// 最大频率
    vector<int> result;
    TreeNode* pre = NULL;
    void traversal(TreeNode* cur) {
        if (cur == NULL) return;
        traversal(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);
        }
        traversal(cur->right);// 右
        return;
    }
    vector<int> findMode(TreeNode* root) {
        traversal(root);
        return result;
    }
};

不是二叉搜索树,最直观的方法把这个树都遍历,用map统计频率,把频率排个序,最后取前面高频的元素的集合

(1)遍历这个树,用map统计频率

用哪种遍历顺序都可以

// map<int, int> key:元素,value:出现频率
void traversal(TreeNode* cur, unordered_map<int, int>& map) {// 前序遍历
	if (cur == NULL) return;
	map[cur->val]++;
	traversal(cur->left, map);
	traversal(cur->right, map);
	return;
}

(2)把统计的出来的出现频率(即map中的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++) {
	if (vec[i].second == vec[0].second) result.push_back(vec[i].first);
	else break;
}
return result;

C++:

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

3 二叉树的最近公共祖先

236. 二叉树的最近公共祖先
在这里插入图片描述最近公共祖先的定义:

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

思路:
二叉树如何可以自底向上查找?回溯

后序遍历(左右中)就是天然的回溯,可以根据左右子树的返回值,来处理中节点的逻辑

最容易的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先
在这里插入图片描述

判断逻辑:如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果左右子树的返回值都不为空,说明此时的中节点,一定是q 和p的最近祖先

容易忽略的情况:
在这里插入图片描述

递归法
(1)确定递归函数返回值以及参数
返回值:最近公共节点,TreeNode*
参数:根节点、p、q

TreeNode* traversal(TreeNode* root, TreeNode* p, TreeNode* q)

(2)确定终止条件

遇到空的话,因为树都是空了,所以返回空
如果 root == q,或者 root == p,说明找到 q p ,则将其返回

if (root == q || root == q || root == NULL) return root;

(3)确定单层递归逻辑

本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题依然要遍历树的所有节点

如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?

  1. 搜索一条边
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
  1. 搜索整棵树
left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理;         // 中 

在这里插入图片描述

TreeNode* left = traversal(root->left, p, q);
TreeNode* right = traversal(root->right, p, q);

(1) 如果left和right都不为空,说明此时root就是最近公共节点
(2) 如果left为空,right不为空,就返回right,说明目标节点是通过right返回的。

在这里插入图片描述

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

寻找最小公共祖先,完整流程图:
在这里插入图片描述
C++:

class Solution {
public:
    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;
        if (left == NULL && right != NULL) return right;
        else if (left != NULL && right == NULL) return left;
        else return NULL;
    }
};

精简C++:

class Solution {
public:
    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;
        if (left == NULL) return right;
        return left;
    }
};

总结:

(1)求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式
(2)在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断


鼓励坚持十八天的自己😀😀😀

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch 索引生命周期和翻滚 (rollover) 策略

Elasticsearch 是搜索引擎中的摇滚明星&#xff0c;它的蓬勃发展在于使你的数据井井有条且速度快如闪电。 但当你的数据成为一场摇滚音乐会时&#xff0c;管理其生命周期就变得至关重要。 正确使用索引生命周期管理 (ILM) 和 rollover 策略&#xff0c;你的后台工作人员可确保顺…

【JVM从入门到实战】(八)垃圾回收(1)

内存泄漏&#xff1a;指的是不再使用的对象在系统中未被回收&#xff0c;内存泄漏的积累可能会导致内存溢出 什么是垃圾回收 Java中为了简化对象的释放&#xff0c;引入了自动的垃圾回收&#xff08;Garbage Collection简称GC&#xff09;机制。通过垃 圾回收器来对不再使用的…

20231218在微软官网下载WINDOWS10以及通过rufus-4.3p写入U盘作为安装盘

20231218在微软官网下载WINDOWS10以及通过rufus-4.3p写入U盘作为安装盘 2023/12/18 17:06 百度搜索&#xff1a;下载 windows10 https://www.microsoft.com/zh-cn/software-download/windows10 下载 Windows 10 更新之前&#xff0c;请参阅 Windows 版本信息状态中的已知问题&a…

STM32 CAN多节点组网项目实操 挖坑与填坑记录2

系列文章&#xff0c;持续探索CAN多节点通讯&#xff0c; 上一篇文章链接&#xff1a; STM32 CAN多节点组网项目实操 挖坑与填坑记录-CSDN博客文章浏览阅读120次。CAN线性组网项目开发过程中遇到的数据丢包问题&#xff0c;并尝试解决的记录和推测分析。开发了一个多节点线性…

【可用性】Redis作为注册中心配合Spring Task的高可用案例

需求&#xff1a; 假设当前有一个短信服务是多节点集群部署&#xff0c;我们希望每个服务节点在启动时能将服务信息"注册"到redis缓存中&#xff0c;所有服务节点每隔3分钟上报一次&#xff0c;表示当前服务可用。每个服务还会作为哨兵节点每隔10分钟查询一次redis&a…

I.MX6ULL_Linux_驱动篇(47)linux RTC驱动

RTC 也就是实时时钟&#xff0c;用于记录当前系统时间&#xff0c;对于 Linux 系统而言时间是非常重要的&#xff0c;就和我们使用 Windows 电脑或手机查看时间一样&#xff0c;我们在使用 Linux 设备的时候也需要查看时间。本章我们就来学习一下如何编写 Linux 下的 RTC 驱动程…

小程序自定义轮播图样式

小程序自定义轮播图样式以下是各案例&#xff0c;仅供大家参考。 效果展示&#xff1a; index.wxml代码&#xff1a; <view><!-- 轮播 --><view><swiper indicator-dots"{{indicatorDots}}"autoplay"{{autoplay}}" interval"{{…

易点易动:实现固定资产账实一致和一站式管理的财务系统打通

在当今竞争激烈的商业环境中&#xff0c;企业需要高效管理其固定资产&#xff0c;确保资产账实一致&#xff0c;并实现一站式管理。易点易动是一种集成的财务系统&#xff0c;它通过打通各个环节&#xff0c;提供了一种便捷的方式来管理固定资产。本文将探讨易点易动系统的优势…

基于ssm大学学生成长系统论文

摘 要 随着互联网技术的发展&#xff0c;各类网站应运而生&#xff0c;网站具有新颖、展现全面的特点。因此&#xff0c;为了满足阜阳师范大学学生成长管理的需求&#xff0c;特开发了本阜阳师范大学学生成长系统。 本阜阳师范大学学生成长系统采用Java技术&#xff0c;基于SS…

Axure之中继器的使用(交互动作reperter属性Item属性)

目录 一.中继器的基本使用 二.中继器的动作&#xff08;增删改查&#xff09; 2.1 新增 2.2 删除 2.3 更新行 2.4 效果展示 2.5 模糊查询 三.reperter属性 在Axure中&#xff0c;中继器&#xff08;Repeater&#xff09;是一种功能强大的组件&#xff0c;用于创建重复…

AttributeError: module ‘jax‘ has no attribute ‘Array‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)更改应用名称

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;更改应用名称 一、操作环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、更改应用名称(HAP) 更改位置如下&#xff1a;entry->src->main->modul…

C++ 字符串输入cin、cin.get()、cin.getlin()

程序string.cpp有一个缺陷&#xff0c;这种缺陷通过精心选择输入被掩盖掉了。 如下示例码&#xff1a; // Len_char.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include <iostream> using namespace std;#define SIZE 20 int main() {c…

水利部提前下达补助资金,推进小型水库除险加固!

为加快推进小型水库除险加固前期工作&#xff0c;水利部协调财政部提前下达了2023年度中央补助资金&#xff0c;对小型水库实施除险加固。加快构建气象卫星和测雨雷达、雨量站、水文站组成的雨情、水情监测防线&#xff0c;大力推进数字孪生水利建设&#xff0c;提升流域防洪数…

云端开炉,线上训练,Bert-vits2-v2.2云端线上训练和推理实践(基于GoogleColab)

假如我们一定要说深度学习入门会有一定的门槛&#xff0c;那么设备成本是一个无法避开的话题。深度学习模型通常需要大量的计算资源来进行训练和推理。较大规模的深度学习模型和复杂的数据集需要更高的计算能力才能进行有效的训练。因此&#xff0c;训练深度学习模型可能需要使…

基于ssm个性化美食推荐系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本个性化美食推荐系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…

【高项】五组十域 案例分析

五组 启动-规划-执行-监控-收尾 &#xff08;启规执监守&#xff09; 十域 范-范围 进-进度 戏-干系人 狗-沟通 子&#xff08;zhi&#xff09;-质量 成-成本 才-采购 整-整合-整体 风-风险 资-资源 案例分析解题思路 定位/分析题型/套用子过程 定位 先看问题&…

C++设计模式之——命令模式

命令模式 概念创建步骤示例示例一代码实现运行结果 示例二代码实现运行结果 示例三示例代码运行结果 示例四代码实现运行结果 应用场景 概念 命令模式是一种行为型设计模式&#xff0c;它允许将请求封装为一个对象&#xff0c;从而使得可以参数化客户端请求、将请求排队或者记…

【数据结构】八大排序之简单选择排序算法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.简单选择排序简介及思路 二.简单选择排序的代码实现 三.简单选择排序的优化 四.简单选择排序的时间复杂度分析 结语 一.简单选择排序简介及思路 简单选择排序算法…

k8s集群内部署nexus

一、前言 在k8s集群中部署nexus服务需要使用到pv、pvc服务来存储nexus的数据&#xff0c;需要使用service服务来提供对外访问nexus服务的端口&#xff0c;需要使用deployment服务来管理nexus服务&#xff0c;接下来就是用这些服务来在k8s集群中搭建nexus&#xff0c;pv服务使用…