代码随想录算法训练营第16天 | 第六章 二叉树 part06

2024-08-26 07:44

本文主要是介绍代码随想录算法训练营第16天 | 第六章 二叉树 part06,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

代码随想录算法训练营第16天 | 第六章 二叉树 part06

    • 530.二叉搜索树的最小绝对差
    • 501.二叉搜索树中的众数
      • 普通二叉树
      • 搜索二叉树
    • 236. 二叉树的最近公共祖先

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

需要领悟一下二叉树遍历上双指针操作,优先掌握递归
题目链接/文章讲解:link
视频讲解:link

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int result =INT_MAX;TreeNode* pre;void traversal(TreeNode* cur){if (cur==nullptr)return;traversal(cur->left);if (pre!=nullptr)result=abs(cur->val-pre->val)>result?result:abs(cur->val-pre->val);pre=cur;traversal(cur->right);}int getMinimumDifference(TreeNode* root) {traversal(root);return result;}
};

中序遍历能够按照递增顺序访问节点的值。因此,最小绝对差一定是相邻节点之间的差值,写的时候才注意到,完全没必要用abs函数求绝对值。

int getMinimumDifference(TreeNode* root) {stack<TreeNode*> stk;TreeNode* cur = root;TreeNode* pre = nullptr;int result = INT_MAX;while (!stk.empty() || cur != nullptr) {while (cur != nullptr) {stk.push(cur);cur = cur->left;}cur = stk.top();stk.pop();  if (pre != nullptr) {result = min(result, abs(cur->val - pre->val));}pre = cur;        cur = cur->right;}return result;
}

使用栈来实现中序遍历的迭代版本,从而避免栈溢出问题。同时避免代码中使用了全局变量 pre 和 result,这可以将它们作为函数参数传递=。

501.二叉搜索树中的众数

和 530差不多双指针思路,不过 这里涉及到一个很巧妙的代码技巧。
如果不是二叉搜索树,我也想到的最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。
可以先自己做做看,然后看视频讲解。

普通二叉树

这也是一个难题,如果树中有不止一个众数,可以按 任意顺序 返回。遍历 unordered_map,寻找频率最大的节点值。使用 keyVector.clear() 清空之前的众数,如果发现有更新的更大频率值,就将新的值加入 vector。如果遇到相同频率的值,则追加到 keyVector

link
视频讲解:link

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:
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 ;
}                  vector<int> findMode(TreeNode* root) {std::unordered_map<int, int> map;searchBST(root, map);int maxKey = map.begin()->second;  // 初始将第一个键作为最大值std::vector<int> keyVector; // 遍历unordered_mapfor (const auto& pair : map) {if (pair.second > maxKey) {maxKey = pair.second;     keyVector.clear();   keyVector.push_back(pair.first);  } else if (pair.second == maxKey) {keyVector.push_back(pair.first);}}return keyVector;}
};

使用 unordered_map<int, int> 来存储每个节点值以及它出现的次数。map[cur->val]++ 这一步递增节点值 val 的频率。

搜索二叉树

想到搜索二叉树,首先想到中序遍历,每一次 弄一个指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组).频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了。
count = 0;
maxCount = 0;
pre = NULL;
result.clear();//看了代码随想录的代码,确实不能忘了全局变量初始化,因为我们写的是子函数,得要被调用很多次。第一次被用完,再一次调用要记得初始化。

代码看着挺麻烦,实际上只要把思路给搞懂,仅仅是麻烦不复杂。中序遍历,判断最大清空或者导入数组,就这两部分。外加上全局变量的设定和初始化。

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     TreeNode *left;*     TreeNode *right;*     TreeNode() : val(0), left(nullptr), right(nullptr) {}*     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}*     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:
TreeNode* pre;
int maxCount = 0; 
int count = 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中result.push_back(cur->val);}if (count > maxCount) { // 如果计数大于最大值频率maxCount = count;  result.clear();     result.push_back(cur->val);}      searchBST(cur->right);      return;}vector<int> findMode(TreeNode* root) {count = 0;maxCount = 0;pre = nullptr; // 记录前一个节点result.clear();searchBST(root);return result;}
};

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

本题其实是比较难的,可以先看视频讲解

link
视频讲解:link
先想下思路如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。
在这里插入图片描述
最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从底向上的遍历方式。

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

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

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

这题很巧妙,如果当前节点是 p 或 q,或者当前节点为空(root == NULL),直接返回当前节点作为结果。当前节点如果是其中一个目标节点,说明找到了一个目标节点,此时可能是公共祖先。 递归地在左子树 root->left 和右子树 root->right 上分别查找目标节点 p 和 q,将结果分别存储在 left 和 right。就是一层层往下查。 如果发现左边找到了一个,右边找到了一个,那就返回当前结点。 如果发现都是在左边找到的如果 left != NULL && right != NULL,说明 p 和 q 分别在当前节点的左右子树中,所以当前节点 root 就是它们的最近公共祖先。 如果 left == NULL && right != NULL,说明 p 和 q 都在右子树中,返回 right 作为结果。 如果 left != NULL && right == NULL,说明 p 和 q 都在左子树中,返回 left 作为结果。 如果 left == NULL && right == NULL,说明在当前节点的子树中没有找到 p 或 q,返回 NULL。

这篇关于代码随想录算法训练营第16天 | 第六章 二叉树 part06的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1107962

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma

深入解析 Java Future 类及代码示例

《深入解析JavaFuture类及代码示例》JavaFuture是java.util.concurrent包中用于表示异步计算结果的核心接口,下面给大家介绍JavaFuture类及实例代码,感兴... 目录一、Future 类概述二、核心工作机制代码示例执行流程2. 状态机模型3. 核心方法解析行为总结:三

python获取cmd环境变量值的实现代码

《python获取cmd环境变量值的实现代码》:本文主要介绍在Python中获取命令行(cmd)环境变量的值,可以使用标准库中的os模块,需要的朋友可以参考下... 前言全局说明在执行py过程中,总要使用到系统环境变量一、说明1.1 环境:Windows 11 家庭版 24H2 26100.4061

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

MySQL 添加索引5种方式示例详解(实用sql代码)

《MySQL添加索引5种方式示例详解(实用sql代码)》在MySQL数据库中添加索引可以帮助提高查询性能,尤其是在数据量大的表中,下面给大家分享MySQL添加索引5种方式示例详解(实用sql代码),... 在mysql数据库中添加索引可以帮助提高查询性能,尤其是在数据量大的表中。索引可以在创建表时定义,也可

使用C#删除Excel表格中的重复行数据的代码详解

《使用C#删除Excel表格中的重复行数据的代码详解》重复行是指在Excel表格中完全相同的多行数据,删除这些重复行至关重要,因为它们不仅会干扰数据分析,还可能导致错误的决策和结论,所以本文给大家介绍... 目录简介使用工具C# 删除Excel工作表中的重复行语法工作原理实现代码C# 删除指定Excel单元