Leetcode算法训练日记 | day21

一、二叉搜索树的最小绝对差

1.题目

Leetcode:第 530 题

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

示例 1:

输入:root = [4,2,6,1,3]
输出:1

示例 2:

输入:root = [1,0,48,null,null,12,49]
输出:1

2.解题思路

1.一般递归法:使用递归法遍历整个二叉树,得出保存所有节点元素的数组,再遍历数组找出最小差值。

2.双指针递归法:通过栈来进行二叉树的遍历,模拟了二叉树的中序遍历。在遍历过程中,始终保持一个指向前一个访问节点的指针 pre,并在每次访问新节点时更新最小差值 result。由于栈的特性,可以保证在每次从栈中弹出节点时,都已经访问过它的所有左子树节点,因此我们可以计算当前节点与前一个节点的差值。这样,可以在一次遍历中找到所有节点值之间的最小差值。

3.迭代法:使用迭代法遍历整个二叉树,找出不同节点值之间的最小差值 。

3.实现代码

// 一、实现计算二叉树最小差值的算法(一般递归法)。
class Solution1 {
public:vector<int> vec; // 定义一个vec,用于存储二叉树节点的值,// 定义一个名为traversal的私有成员函数,用于执行二叉树的中序遍历。// 参数root是当前遍历到的二叉树节点指针。void traversal(TreeNode* root) {if (root == NULL) return; // 如果当前节点为空,直接返回,不执行任何操作。traversal(root->left); // 递归调用traversal函数遍历当前节点的左子树。vec.push_back(root->val); // 访问当前节点,将其值添加到vec中。traversal(root->right); // 递归调用traversal函数遍历当前节点的右子树。}// 定义一个名为getMinimumDifference的公共成员函数,用于计算给定二叉树中所有节点值之间的最小差值。// 参数root是二叉树的根节点指针。int getMinimumDifference(TreeNode* root) {vec.clear(); // 清空vec,为新的中序遍历做准备。traversal(root); // 调用traversal函数,传入根节点,执行中序遍历,并将结果存储在vec中。if (vec.size() < 2) return 0;  // 检查vec中的元素数量,如果小于2,则不存在有效的差值,返回0。int result = INT_MAX;// 初始化最小差值result为INT_MAX,表示最大的整数值。for (int i = 1; i < vec.size(); i++) {// 遍历vec中的元素,计算并更新最小差值。result = min(result, vec[i] - vec[i - 1]);   // 计算当前元素与前一个元素的差值,并与已知的最小差值result进行比较。}return result; // 返回计算得到的最小差值。}
};//  二、实现计算二叉树最小差值的算法(双指针递归法)。
class Solution2 {
public:int result = INT_MAX; // 初始化结果变量result为整数的最大值INT_MAX,表示开始时最小差值为无穷大。TreeNode* pre = NULL; // 定义一个指向TreeNode的指针pre,用于记录遍历过程中前一个访问的节点。// 定义一个名为traversal的私有成员函数,用于执行二叉树的遍历。// 参数cur是当前遍历到的二叉树节点指针。void traversal(TreeNode* cur) {if (cur == NULL) return; // 如果当前节点为空,直接返回,不执行任何操作。traversal(cur->left); // 递归调用traversal函数遍历当前节点的左子树。if (pre != NULL) {  // 如果pre不为空,说明我们已经遍历过至少一个节点,可以计算差值。result = min(result, cur->val - pre->val);// 更新结果变量result为当前节点值与前一个节点值之差的最小值。}pre = cur; // 更新pre为当前节点,因为在下一次递归调用中,当前节点将成为新的前一个节点。traversal(cur->right);  // 递归调用traversal函数遍历当前节点的右子树。}// 定义一个名为getMinimumDifference的公共成员函数,用于启动遍历过程并返回最小差值。// 参数root是二叉树的根节点指针。int getMinimumDifference(TreeNode* root) {traversal(root);// 调用traversal函数,传入根节点,开始遍历二叉树。return result; // 返回结果result,即二叉树中所有节点值之间的最小差值。}
};//  三、实现计算二叉树最小差值的算法(迭代法)。
class Solution3 {
public:// 定义一个名为getMinimumDifference的公共成员函数,用于计算给定二叉树中所有节点值之间的最小差值。// 参数root是二叉树的根节点指针。int getMinimumDifference(TreeNode* root) {stack<TreeNode*> st; // 定义一个栈st,用于存放二叉树的节点指针,辅助进行迭代遍历。TreeNode* cur = root; // 定义一个当前节点指针cur,初始指向根节点。TreeNode* pre = NULL; // 定义一个前一个节点指针pre,初始为NULL,用于记录遍历过程中的前一个节点值。int result = INT_MAX; // 初始化结果result为整数的最大值INT_MAX,表示开始时最小差值为无穷大。while (cur != NULL || !st.empty()) { // 当当前节点不为空,或者栈st不为空时,继续遍历。if (cur != NULL) { // 如果当前节点不为空,执行以下操作:st.push(cur); // 将当前节点压入栈st。cur = cur->left; // 将cur更新为当前节点的左子节点,准备遍历左子树。}else { // 如果当前节点为空,执行以下操作:cur = st.top(); // 将栈顶节点作为当前节点。st.pop(); // 弹出栈顶节点。// 如果pre不为空,说明我们之前已经访问过至少一个节点,可以计算差值。if (pre != NULL) {result = min(result, cur->val - pre->val); // 更新结果result为当前节点值与前一个节点值之差的最小值。}pre = cur; // 更新pre为当前节点,因为在下一次迭代中,当前节点将成为新的前一个节点。cur = cur->right; // 将cur更新为当前节点的右子节点,准备遍历右子树。}}return result; // 返回结果result,即二叉树中所有节点值之间的最小差值。}
};

二、二叉搜索树中的众数

1.题目

Leetcode:第 501 题

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

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

假定 BST 满足如下定义:

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

示例 1:

输入:root = [1,null,2,2]
输出:[2]

示例 2:

输入:root = [0]
输出:[0]
2.解题思路

通过中序遍历二叉搜索树,并在遍历过程中跟踪连续出现相同元素的次数。如果遇到不同的元素,或者遍历结束,则将当前的连续元素数量与最大连续元素数量进行比较。如果相等,就将该元素的值添加到结果向量中。最终,findMode 函数返回包含出现次数最多元素的数组。

3.实现代码
#include <iostream>
#include <vector>
#include <stack>
using namespace std;// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {int val; // 存储节点的值。TreeNode* left; // 指向该节点左子树的指针。TreeNode* right; // 指向该节点右子树的指针。// TreeNode的构造函数,用于创建一个TreeNode实例。// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 一、查找二叉搜索树中的众数(递归法)。
class Solution1 {
public:int maxCount = 0; // 定义一个整数maxCount,用于存储遍历过程中遇到的最长连续元素的数量。int count = 0;// 定义一个整数count,用于存储当前连续出现的元素数量。TreeNode* pre = NULL; // 定义一个指向TreeNode的指针pre,用于记录遍历过程中前一个访问的节点。vector<int> result; // 定义一个容器result,用于存储出现次数最多的元素。// 定义一个名为searchBST的私有成员函数,用于递归遍历二叉搜索树。// 参数cur是当前遍历到的二叉树节点指针。void searchBST(TreeNode* cur) {if (cur == NULL) return; // 如果当前节点为空,直接返回。searchBST(cur->left); // 递归遍历当前节点的左子树。// 如果pre为空(即这是遍历的起始节点),或者当前节点的值与pre相同(即连续出现),则增加count。if (pre == NULL) {count = 1;}else if (pre->val == cur->val) {count++;}else {count = 1; // 否则,重置count为1。}pre = cur; // 更新pre为当前节点。// 如果当前的连续元素数量count等于最大连续元素数量maxCount,则将当前节点的值添加到结果result中。if (count == maxCount) {result.push_back(cur->val);}if (count > maxCount) { // 如果计数大于最大值频率maxCount = count;   // 更新最大频率result.clear();     // 清空result,之前result里的元素都失效了result.push_back(cur->val);}searchBST(cur->right); // 递归遍历当前节点的右子树。return; // 这里返回是为了结束函数调用,实际上在递归中不需要显式返回。}// 定义一个名为findMode的公共成员函数,用于启动搜索过程并返回出现次数最多的元素列表。// 参数root是二叉树的根节点指针。vector<int> findMode(TreeNode* root) {count = 0; // 重置当前连续元素数量。maxCount = 0; // 重置最大连续元素数量。pre = NULL; // 重置前一个访问的节点指针。result.clear(); // 清空结果,为新的搜索做准备。searchBST(root); // 调用searchBST函数,传入根节点,开始递归遍历。return result;// 返回结果result,包含出现次数最多的元素。}
};// 二、查找二叉搜索树中的众数(迭代法)。
class Solution2 {
public:// 定义一个名为findMode的公共成员函数,用于启动搜索过程并返回出现次数最多的元素列表。// 参数root是二叉树的根节点指针。vector<int> findMode(TreeNode* root) {stack<TreeNode*> st; // 定义一个栈st,用于存放二叉树的节点指针,辅助进行迭代遍历。TreeNode* cur = root; // 定义一个当前节点指针cur,初始指向根节点。TreeNode* pre = NULL; // 定义一个前一个节点指针pre,初始为NULL,用于记录遍历过程中的前一个节点值。int maxCount = 0; // 初始化最大连续元素数量maxCount为0。int count = 0; // 初始化当前连续元素数量count为0。vector<int> result; // 定义一个容器result,用于存储出现次数最多的元素。// 当当前节点不为空,或者栈st不为空时,继续遍历。while (cur != NULL || !st.empty()) {if (cur != NULL) {st.push(cur); // 如果当前节点不为空,将当前节点压入栈st。cur = cur->left; // 将cur更新为当前节点的左子节点,准备遍历左子树。}else {cur = st.top(); // 如果当前节点为空,将栈顶节点作为当前节点。st.pop(); // 弹出栈顶节点。// 如果pre为空(即这是遍历的起始节点),则重置count为1。if (pre == NULL) {count = 1;}// 如果当前节点的值与pre相同(即连续出现),则增加count。else if (pre->val == cur->val) {count++;}else {count = 1; // 否则,重置count为1。}pre = cur; // 更新pre为当前节点。// 如果当前的连续元素数量count等于最大连续元素数量maxCount,则将当前节点的值添加到结果result中。if (count == maxCount) {result.push_back(cur->val);}// 如果当前的连续元素数量count大于最大连续元素数量maxCount,则更新maxCount并清空result,将当前节点的值添加到result中。if (count > maxCount) {maxCount = count;result.clear();result.push_back(cur->val);}cur = cur->right; // 将cur更新为当前节点的右子节点,准备遍历右子树。}}return result; // 返回结果result,包含出现次数最多的元素。}
};

三、二叉树的最近公共祖先

1.题目

Leetcode:第 236 题

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

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

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。

示例 2:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。

示例 3:

输入:root = [1,2], p = 1, q = 2
输出:1
2.解题思路

使用递归法遍历二叉树,找到p和q节点,并求出最近公共祖先。

3.实现代码
#include <iostream>
#include <vector>
using namespace std;// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {int val; // 存储节点的值。TreeNode* left; // 指向该节点左子树的指针。TreeNode* right; // 指向该节点右子树的指针。// TreeNode的构造函数,用于创建一个TreeNode实例。// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};// 寻找二叉树的最近公共祖先(递归法)
class Solution {
public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {// 如果当前节点root是节点q或p之一,或者root为空,则返回当前节点root作为可能的最近公共祖先。if (root == q || root == p || root == NULL) return root;// 递归调用lowestCommonAncestor函数在当前节点root的左子树中查找p和q的最近公共祖先,并将结果存储在left。TreeNode* left = lowestCommonAncestor(root->left, p, q);// 递归调用lowestCommonAncestor函数在当前节点root的右子树中查找p和q的最近公共祖先,并将结果存储在right。TreeNode* right = lowestCommonAncestor(root->right, p, q);// 如果在左右子树中都找到了p和q的最近公共祖先(即left和right都不为空),则当前节点root是p和q的最近公共祖先。if (left != NULL && right != NULL) return root;// 如果只在右子树中找到了p和q的最近公共祖先(即left为空,right不为空),则返回right。if (left == NULL && right != NULL) return right;// 如果只在左子树中找到了p和q的最近公共祖先(即left不为空,right为空),则返回left。else if (left != NULL && right == NULL) return left;// 如果在左右子树中都没有找到p和q的最近公共祖先(即left和right都为空),或者p和q都位于当前节点的同一侧(左或右),则返回NULL。else {return NULL;}}
};

ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。

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

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

相关文章

mapv修改源码实现图标和管道到统一页面显示,图标和管道和点击

一、效果图 二、背景 map 地图添加marker&#xff0c;是操作的dom&#xff0c;而mapv是使用的canvas方式&#xff0c;所以性能要好 三、Mapv和MapVGL的区别 百度地图 JavaScript API GL快速升级 和mapVGL的使用 Mapv 是一款基于百度地图的大数据可视化开源库&#xff0c;可以…

【Linux】socket编程2

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;客户端代码Makefile(生成目标文件)UdpClient.cc(客户端代码)服务端代码部分优化1&#xff08;接受客户端时显示客…

ffmpeg 将多个视频片段合成一个视频

ffmpeg 将多个视频片段合成一个视频 References 网络视频 6 分钟的诅咒。 新建文本文件 filelist.txt filelist.txtfile output_train_video_0.mp4 file output_train_video_1.mp4 file output_train_video_2.mp4 file output_train_video_3.mp4 file output_train_video_4.m…

淘宝扭蛋机小程序源码搭建:打造专属电商娱乐新平台

在数字化浪潮的推动下&#xff0c;电商平台不断创新&#xff0c;以满足消费者日益多样化的需求。淘宝扭蛋机小程序作为一种创新的电商娱乐形式&#xff0c;受到了广大用户的热烈追捧。为了满足市场需求&#xff0c;许多企业和开发者开始关注淘宝扭蛋机小程序的源码搭建&#xf…

Spring6-单元测试:JUnit

1. 概念 在进行单元测试时&#xff0c;特别是针对使用了Spring框架的应用程序&#xff0c;我们通常需要与Spring容器交互以获取被测试对象及其依赖。传统做法是在每个测试方法中手动创建Spring容器并从中获取所需的Bean。以下面的两行常见代码为例&#xff1a; ApplicationCo…

[C语言][数据结构][链表] 单链表的从零实现!

目录 零.必备知识 1.一级指针 && 二级指针 2. 节点的成员列表 a.数据 b.指向下一个节点的指针. 3. 动态内存空间的开辟 (malloc-calloc-realloc) 一.单链表的实现与销毁 1.1 节点的定义 1.2 单链表的尾插 1.3 单链表的头插 1.4 单链表的尾删 1.5 单链表的头删 1…

挑错罐头=“害猫”!猫咪主食罐到底应该怎么选?

猫咪罐头已经成为众多猫奴们的喂养首选。它富含水分&#xff0c;有助于猫咪保持良好的泌尿系统健康&#xff0c;尤其对于那些不太喜欢饮水的猫咪来说&#xff0c;罐头无疑是补充水分的理想方式。罐头的口感极佳&#xff0c;肉质细腻&#xff0c;能够激发猫咪的食欲&#xff0c;…

C语言—每日选择题—Day65

前言 我们的刷题专栏又又又开始了&#xff0c;本专栏总结了作者做题过程中的好题和易错题。每道题都会有相应解析和配图&#xff0c;一方面可以使作者加深理解&#xff0c;一方面可以给大家提供思路&#xff0c;希望大家多多支持哦&#xff5e; 第一题 1、如下代码输出的是什么…

深入理解LRU缓存算法:原理、应用与优化

LRU算法&#xff08;Least Recently Used&#xff0c;最近最少使用算法&#xff09;的思想是基于"时间局部性"原理&#xff0c;即在一段时间内&#xff0c;被访问过的数据在未来仍然会被频繁访问的概率较高。 LRU 原理 LRU算法的主要思想是将最近被使用的数据保留在…

UEditor编辑器自动将div标签转换成p标签应该如何解决 ———————————————— 版权声明:本文为博主原创文章,遵循 CC 4

首先在ueditor的文件夹下找到ueditor.all.js文件&#xff0c;然后搜索allowDivTransToP找到这段代码&#xff0c;把后面的true设置为false 接着在ueditor.config.js文件内搜索allowDivTransToP找到如下的代码&#xff0c;将注释去掉并且改为false //默认过滤规则相关配置项目/…

MPT - 原理及应用

前文回顾 Merkle原理及应用Merkle代码实现Patricia原理及应用Patricia代码实现 什么是MPT&#xff08;Merkle Patricia Tree&#xff09;树 MPT树是一种数据结构&#xff0c;用于在以太坊区块链中高效地存储和检索账户状态、交易历史和其他重要数据。MPT树的设计旨在结合Merk…

sqlmap(四)案例

一、注入DB2 http://124.70.71.251:49431/new_list.php?id1 这是墨者学院里的靶机&#xff0c;地址&#xff1a;https://www.mozhe.cn/ 1.1 测试数据库类型 python sqlmap.py -u "http://124.70.71.251:49431/new_list.php?id1" 1.2 测试用户权限类型 查询选…

常见深度学习之十二大激活函数【函数定义、性质、数学公式、代码实现】

目录 前言 1、激活函数的定义与作用 2、激活函数的性质 二、常见的激活函数 2.1 Sigmoid函数&#xff1a; 1. 作用 2. 优点 3. 缺点 4. 数学公式 5.Sigmoid函数实现及可视化图像 2.2 Tanh函数 1. 函数定义 2.优点 3.缺点 4.Tanh函数实现及可视化图像 2.3ReLU 函数 &#xff1a;…

物联网实战--驱动篇之(二)Modbus协议

目录 一、modbus简介 二、功能码01、02 三、modbus解析 四、功能码03、04 五、功能码05 六、功能码06 七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂&#xff0c;modbus-RTU ASCII TCP等等&#xff0c;还有跟PLC结合的&#xff0c;地址还分1开…

C语言进阶课程学习记录-第29课 - 指针和数组分析(下)

C语言进阶课程学习记录-第29课 - 指针和数组分析&#xff08;下&#xff09; 数组名与指针实验-数组形式转换实验-数组名与指针的差异实验-转化后数组名加一的比较实验-数组名作为函数形参小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课…

【JavaWeb】Day39.MySQL概述——数据库设计-DQL(二)

数据库设计-DQL 聚合函数 聚合函数查询就是纵向查询&#xff0c;它是对一列的值进行计算&#xff0c;然后返回一个结果值。&#xff08;将一列数据作为一个整体&#xff0c;进行纵向计算&#xff09; 语法&#xff1a; select 聚合函数(字段列表) from 表名 ; 注意 : 聚合…

软件设计—接口安全设计规范

1.token授权机制 2.https传输加密 3.接口调用防滥用 4.日志审计里监控 5.开发测试环境隔离&#xff0c;脱敏处理 6.数据库运维监控审计 软件项目相关全套精华资料包获取方式①&#xff1a;点我获取 获取方式②&#xff1a;本文末个人名片直接获取。

高校人事管理系统业务分析

目标用户 大学人事部门&#xff0c;部门、院系、任务 解决问题 人事部门按业务划分了很多科室、数据分散、工作流程杂乱、工作效率低。 主要功能模块 人事综合管理平台、个人自助服务平台、人才招聘管理系统、薪酬管理子系统、职称评审子系统、绩效考核子系统组成。

泛零售行业大会员经营的业务挑战与应对策略

​泛零售企业发展到成规模阶段一定会沉淀大量会员&#xff0c;在当前的市场竞争下&#xff0c;企业的经营重点在关注增量市场的同时&#xff0c;也会聚焦对存量会员的价值深挖&#xff0c;提升会员忠诚度&#xff0c;实现“以客户体验为中心、以数据驱动运营”。 对于多业态、…

小程序打开空白的问题处理

小程序打开是空白的&#xff0c;如下&#xff1a; 这个问题都是请求域名的问题&#xff1a; 一、检查服务器域名配置了 https没有&#xff0c;如果没有&#xff0c;解决办法是申请个ssl证书&#xff0c;具体看这里 https://doc.crmeb.com/mer/mer2/4257 二、完成第一步后&#…