代码随想录算法训练营Day20|最大二叉树、合并二叉树、二叉搜索树中的搜索、验证二叉搜索树

最大二叉树

题目:

最大二叉树定义:

二叉树的根是数组中的最大元素

左子树是通过数组中最大值左边部分构造出的最大二叉树。

右子树是通过数组中最大值右边部分构造出的最大二叉树。

通过给定的数组构建最大二叉树,并且输出这个树的节点。

构造二叉树一般采用的是前序遍历,因为先构造中间节点,然后构造左子树和右子树。

>>确定递归函数的参数和返回值

参数传入的是存放元素的数组,返回该数组构造的二叉树的头节点,返回类型是指向节点的指针。

TreeNode* constructMaximumBinaryTree(vector<int>& nums)

>>确定终止条件

题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。

那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。这表示一个数组大小是1的时候,构造了一个新的节点,并返回。

TreeNode * node = new TreeNode(0);
if(nums.size()== 1){node->val = nums[0];return node;
}

>>确定单层递归的逻辑

1.找到数组中最大的值和对应的下标,最大的值构造根节点,下标用来下一步分割数组。

2.最大值所在的下标左区间构造左子树,这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。

3.最大值所在的下标右区间 构造右子树 ,这里要判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值。

class Solution{
public:TreeNode* constructMaximumBinaryTree(vector<int>&nums){TreeNode* node = new TreeNode(0);if(nums.size() == 1){node->val = nums[0];return node;}//找到数组中最大的值和对应的下标int maxValue = 0;int maxValueIndex = 0;for(int i = 0;i < nums.size();i++){if(nums[i] > maxValue){maxValue = nums[i];maxValueIndex = i;}}node->val = maxValue;//最大值所在的下标左区间 构造左子树if(maxValueIndex > 0){vector<int>newVec(nums.begin(),nums.begin() + maxValueIndex);node->left = constructMaximumBinaryTree(newVec);}//最大值所在的下标右区间,构造右子树if(maxValueIndex < (nums.size()-1)){vector<int>newVec(nums.begin() + maxValueIndex + 1,nums.end());node->right = constructMaximumBinaryTree(newVec);}return node;}
};

优化:

class Solution{
public:TreeNode* traversal(vector<int>&nums,int left,int right){if(left >= right)return nullptr;int maxValueIndex = left;for(int i = left + 1;i < rigth;++i){if(nums[i] > nums[maxValueIndex])maxValueIndex = i;}TreeNode* root = new TreeNode(nums[maxValueIndex]);root->right = traversal (nums,maxValueIndex + 1,right);return root;}public:TreeNode* constructMaximumBinaryTree(vector<int>&nums){return traversal(nums,0,nums.size());}
};

加if是为了不让空节点进入递归,这样后面终止条件就不需要加入 遇到空节点,如果不用if,后面终止条件就得加上 遇到空节点。

二叉搜索树中的搜索

题目:给定一个二叉搜索树BST的根节点和一个值。在BST中找到节点值等于给定值的节点。返回该节点作为树的子树。如果不存在,返回,NULL。

二叉搜索树:特点有序

A.若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;

B.若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;

C.它的左右子树也为二叉搜索树

本题就是在二叉搜索树中搜索一个节点,看如何遍历。

递归法

1.确定递归函数的参数和返回值

递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。

代码:

TreeNode* searchBST(TreeNode* root,int val)

2.确定终止条件

如果root为空,或者找到这个数值了,就返回root节点。

if(root == NULL || root -> val == val)return root;

3.确定单层递归的逻辑

因为二叉搜索树的节点是有序的,所以可以有方向的去搜索。

如果root -> val > val,搜索左子树,如果root -> val < val,就搜索右子树,最后如果都没有搜索到,就返回NULL。

代码如下:

TreeNode* result = NULL;
if(root -> val > val)result = searchBST(root -> left,val);
if(root -> val < val)result = searchBST(root -> right,val);
return result;
class Solution{
public:TreeNode* searchBST(TreeNode* root,int val){if(root == NULL || root->val == val)return root;TreeNode* result = NULL;if(root->val > val)result = searchBST(root->left,val);if(root->val < val)result = searchBST(root->right,val);return result;}
};

class Solution{
public:TreeNode* searchBST(TreeNode* root,int val){if(root == NULL || root->val == val)return root;if(root->val > val)return searchBST(root->left,val);if(root->val < val)return searchBST(root->right,val);return NULL;}
};

迭代法

由于二叉搜索树的节点具有有序性,可以不使用辅助栈或队列就可以写出迭代法。

对于一般二叉树,递归过程中还有回溯的过程,例如走一个左方向的分支走到头了,那么要掉头,再走右分支。而对于二叉搜索树,不需要回溯,因为节点的有序性会帮我们确定正确的搜索方向。

迭代法代码:

class Solution{
public:TreeNode* searchBST(TreeNode* root,int val){while(root != NULL){if(root->val > val)root = root->left;else if(root->val < val)root = root -> right;else return root;}return NULL;}
};

二叉搜索树的特性记牢,利用它的特性写遍历会很简单。

验证二叉搜索树

题目:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

待判定的特征:

1.节点的左子树只包含小于当前节点的数。

2.节点的右子树只包含大于当前节点的数。

3.所有左子树和右子树自身必须也是二叉搜索树。

思路:

要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。

有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。

递归法

可以递归中序遍历将二叉搜索树变成一个数组,代码:

vector<int>vec;
void traversal(TreeNode* root){if(root == NULL)return;traversal(root->left);vec.push_back(root->val);//将二叉搜素树转换为有序数组traversal(root->right);
}

比较一下,看这个数组是否有序,注意:二叉搜索树中不能有重复元素。

traversal(root);
for(int i = 1;i < vec.size();i++){//注意要小于等于,搜索树里不能有相同的元素
if(vec[i] <= vec[i - 1])return false;
}
return true;

完整代码:

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:bool isValidBST(TreeNode* root){vec.clear();traversal(root);for(int i = 1;i < vec.size();i++){if(vec[i] <= vec[i - 1])return false;}return true;}
}

上面转化成数组,然后判断是否有序,也可以不转换,直接在遍历中判断是否有序

注意点:是左子树“所有”节点小于中间节点,不是一个,右子树同理

样例中最小节点  可能是int的最小值,如果这样使用最小的int来比较也是不行的。

可以初始化元素为long long的最小值。

如果阳历中根节点的val可能是long long 的最小值呢?

递归三部曲:

>>确定递归函数,返回值以及参数

要定义一个long long 的全局变量,用来比较遍历的节点是否有序,因为后台测试数据中有int最小值,所以定义为long long的类型,初始化为long long 最小值。

注意递归函数要有bool类型的返回值,只有寻找一条边或一个节点的时候,递归函数会有bool类型的返回值,

本题中,我们寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整棵树,而如果找到了不符合的节点,就立刻返回

long long maxVal = LONG_MIN;
bool isValidBST(TreeNode* root)

确定终止条件

二叉搜索树可以为空

if(root == NULL)return true;

确定单层递归的逻辑

中序遍历,一直更新maxVal,一旦发现maxVal >= root->val,就返回false,注意元素相同时候也要返回false.

bool left = isValidBST(root->left);
//中序遍历,验证遍历的元素是不是从小到大
if(maxVal < root->val)maxVal = root->val;
else return false;bool right = isValidBST(root->right);
return left && right;

整体代码如下:

class Solution{
public:long long maxVal = LONG_MIN;bool isValidBST(TreeNode* root){if(root == NULL)return true;bool left = isValid(root->left);if(maxVal < root->val)maxVal = root->val;else return false;bool right = isValidBST(root->right);return left && right; }
};

上面 maxVal用的是long long最小值,原因是后台数据有int最小值测试用例。

class Solution{
public:TreeNode* pre =NULL;//用来记录前一个节点bool isValidBST(TreeNode* root){if(root == NULL)return true;bool left = isValidBST(root->left);if(pre != NULL && pre->val >= root->val)retrun false;pre = root;//记录前一个节点bool right = isValidBST(root->right);return left && right;}
};

迭代法

迭代法模拟二叉树中序遍历

迭代法中序遍历稍加改动就可以:

class Solution{
public:bool isValidBST(TreeNode* root){stack<TreeNode*>st;TreeNode* cur = root;TreeNode* pre = NULL;//记录前一个节点while(cur != NULL||st.empty()){if(cur != NULL){st.push(cur);sur = cur->left;}else{cur = st.top();st.pop();if(pre != NULL && cur->val <= pre->val)return false;pre = cur;//保存前一个访问的节点cur = cur->right;}}return true;}
};

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

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

相关文章

【JavaScript】Generator

MDN-Generator Generator对象由生成器函数返回&#xff0c;并且它符合可迭代协议和迭代器协议。 Generator-核心语法 核心语法: 定义生成器函数获取generator对象yield表达式的使用通过for of获取每一个yield的值 // 1. 通过function* 创建生成器函数 function* foo() {//…

Unity DOTween插件常用方法(一)

文章目录 1.1 控制Api1.2 动画Api 1.1 控制Api DOKill DOKill表示停止该物体上所有的Tween动画。DOTween可以同时运行多个Tween&#xff0c;如果需要停止所有正在运行的Tween&#xff0c;可以使用这个方法; 还有一种使用场景&#xff0c;即反复打开某一视图&#xff0c;而该视…

Java技术栈 —— Hadoop入门(二)实战

Java技术栈 —— Hadoop入门&#xff08;二&#xff09; 一、用MapReduce对统计单词个数1.1 项目流程1.2 可能遇到的问题1.3 代码勘误1.4 总结 一、用MapReduce对统计单词个数 1.1 项目流程 (1) 上传jar包。 (2) 上传words.txt文件。 (3) 用hadoop执行jar包的代码&#xff0c;…

【C++】 C++入门 — auto关键字

C入门 auto 关键字1 介绍2 使用细则3 注意事项 Thanks♪(&#xff65;ω&#xff65;)&#xff89;谢谢阅读下一篇文章见&#xff01;&#xff01;&#xff01; auto 关键字 1 介绍 编程时常常需要把表达式的值赋给变量&#xff0c;这就要求在声明变量时清楚地知道表达式的类…

56-主,回调函数,回调函数的参数传参,函数和变量的公私有,特权方法,立即执行函数,闭包(解除引用)

1.回调函数 定义了函数,没有调用函数,但最终执行了。 <script>//回调函数// 定时器setInterval(function(){console.log("a")},1000)// 延迟器setTimeout(function(){console.log("a")},3000)</script> 2.将实参变为函数,将person方法作为…

什么是多态?它和重载有什么区别?

前言 大家好&#xff0c;我是chowley&#xff0c;相信学过编程语言的你&#xff0c;肯定听说过多态和重载两个概念&#xff0c;可多数人对他们之间的区别还是不太清晰&#xff0c;导致同时听到两个词一起出现时会大脑空白&#xff0c;今天我就来详细的介绍一下二者的区别&…

指针的深入理解(四)

这节主要讨论sizeof和strlen的区别&#xff0c;以及一些理解题。 sizeof 求的是对象的大小&#xff0c;深入理解一点就是&#xff1a;这个对象&#xff0c;他一定有一块对应的内存空间。求的就是这一块内存空间。 strlen 只能用来求字符串&#xff0c; 求取的是字符串的长度。…

面试了字节大模型算法岗(实习),快被问哭了。。。。

最近技术群组织了一次算法面试讨论会&#xff0c;今天分享的是一位小伙子的痛苦面试经历&#xff0c;如果你想加入我们的讨论群&#xff0c;见文末。 本次分享的内容如下&#xff1a; 应聘岗位&#xff1a;字节大模型算法实习生 面试轮数&#xff1a;第一轮 整体面试感觉&…

python封装的.exe文件是如何在cmd中获取.xml路径的?

这段日子搞项目算法封装&#xff0c;愁死我。来回改了三遍&#xff0c;总算把相对路径、绝对路径&#xff0c;还有cmd给.exe传参的方式搞懂了。 主要是这个语句 workspace sys.argv[1] sys.argv[]的作用就是,在运行python文件的时候从外部输入参数往文件里面传递参数。 外部就…

CTF盲水印工具:Blind-WaterMark安装

工具下载地址&#xff1a;GitCode - 开发者的代码家园 下载完毕后&#xff0c;只留这些东西就行 接下来需要安装两个依赖&#xff1a; opencv、matplotlib 直接pip install安装的话&#xff0c;工具使用会报错 所以需要到网站里挑选适合的版本进行安装 下载地址&#xff1…

项目:博客

1. 运行环境&#xff1a; 主机 主机名 系统 服务 192.168.223.129 Server_Web Linux Web 192.168.48.131 Server-NFS-DNS Linux NFS/DNS 2. 基础配置 配置主机名&#xff0c;静态IP地址 开启防火墙并配置 部分开启SElinux并配置 服务器之间使用同ntp.aliyun.com进行…

代码随想录算法训练营第二十二天|235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点

235. 二叉搜索树的最近公共祖先 发现规律&#xff1a; 当我们从上向下去递归遍历&#xff0c;第一次遇到 cur节点是数值在[p, q]区间中&#xff0c;那么cur就是p和q的最近公共祖先。 class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, Tr…

在JVM中,Java对象是如何创建、存储和访问的?

在Java虚拟机&#xff08;JVM&#xff09;中&#xff0c;Java对象的创建、存储和访问是Java程序运行的核心部分。这个过程涉及到内存管理、对象模型以及运行时数据区域的概念。 1. Java对象的创建&#xff1a; a. 类加载&#xff1a; 在Java程序运行时&#xff0c;类加载器负…

详讲api网关之kong的基本概念及安装和使用(二)

consul的服务注册与发现 如果不知道consul的使用&#xff0c;可以点击上方链接&#xff0c;这是我写的关于consul的一篇文档。 upstreamconsul实现负载均衡 我们知道&#xff0c;配置upstream可以实现负载均衡&#xff0c;而consul实现了服务注册与发现&#xff0c;那么接下来…

C++算法学习心得七.贪心算法(1)

1.贪心算法理论基础 贪心的本质是选择每一阶段的局部最优&#xff0c;从而达到全局最优。贪心算法并没有固定的套路&#xff0c;唯一的难点就是如何通过局部最优&#xff0c;推出整体最优。最好用的策略就是举反例&#xff0c;如果想不到反例&#xff0c;那么就试一试贪心吧 贪…

leetcode209长度最小的子数组|滑动窗口算法详细讲解学习

滑动窗口是一种基于双指针的一种思想&#xff0c;两个指针指向的元素之间形成一个窗口。 分类&#xff1a;窗口有两类&#xff0c;一种是固定大小类的窗口&#xff0c;一类是大小动态变化的窗口。 简而言之&#xff0c;滑动窗口算法在一个特定大小的字符串或数组上进行操作&…

DevEco Studio 保存自动格式化代码

目标&#xff1a;保存后自动格式化代码 单次快捷键&#xff1a;Ctrl Alt L 步骤一 步骤二

7.2、子集求和问题与背包密码系统

7.2、子集求和问题与背包密码系统 一、数学描述 1.1、第一种描述 20 世纪 70 年代末&#xff0c;默克尔和赫尔曼首次尝试将密码系统建立在一个 NP-完全问题上。他们使用了以下数学问题的一个版本&#xff0c;该问题是对经典knapsack问题的概括。 子集和问题 假设你有一个正…

【Midjourney】AI绘画案例(1)龙年吉祥神兽

说明&#xff1a; 1、文中图片版权均为Midjourney所有&#xff0c;请勿用作商业用途。 2、文中图片均经过 Upscale x 4 处理。 3、由于模型原因&#xff0c;某些图片存在暇玼。 1、吉祥神兽——天马&#xff08;独角兽&#xff09; 天马消灾星。 提示词 Prompt: Sky Unicor…

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…