第二十天| 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

Leetcode 654.最大二叉树

题目链接:654 最大二叉树

题干:给定一个不重复的整数数组nums。最大二叉树可以用下面的算法从nums递归地构建:

  1. 创建一个根节点,其值为nums中的最大值。
  2. 递归地在最大值左边的子数组前缀上构建左子树。
  3. 递归地在最大值右边的子数组后缀上构建右子树。

返回nums构建的最大二叉树

思考一:递归法。终止条件:数组长度为0则返回空,数组长度为1则返回叶子节点。单层递归逻辑:先寻找数组中最大值的下标并创建节点root,按此下标分割数组,递归处理分割后的数组作为root节点的孩子节点。

代码:

class Solution {
public:TreeNode* constructMaximumBinaryTree(vector<int>& nums) {if (nums.size() == 0)   return nullptr;     //无元素返回nullif (nums.size() == 1)   return new TreeNode(nums[0]);       //一个元素返回叶子节点int pivotpos = 0;for (int i = 0; i < nums.size(); i++)       //寻找最大值if (nums[i] > nums[pivotpos])   pivotpos = i;TreeNode* root = new TreeNode(nums[pivotpos]);//分割区间vector<int> leftNum(nums.begin(),nums.begin() + pivotpos);vector<int> rightNum(nums.begin() + pivotpos + 1,nums.end());//组装二叉树root->left = constructMaximumBinaryTree(leftNum);root->right = constructMaximumBinaryTree(rightNum);return root;}
};

思考二:为减少空间利用率,构造新函数,传参区间左右下标。

代码:

class Solution {
private:// 在左闭右开区间[left, right),构造二叉树TreeNode* traversal(vector<int>& nums, int left, int right) {if (left >= right) return nullptr;// 分割点下标:maxValueIndexint maxValueIndex = left;for (int i = left + 1; i < right; ++i)if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;TreeNode* root = new TreeNode(nums[maxValueIndex]);// 组装二叉树root->left = traversal(nums, left, maxValueIndex);root->right = traversal(nums, maxValueIndex + 1, right);return root;}
public:TreeNode* constructMaximumBinaryTree(vector<int>& nums) {return traversal(nums, 0, nums.size());}
};

Leetcode 617.合并二叉树

题目链接:617 合并二叉树

题干:给你两棵二叉树:root1root2

想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为null 的节点将直接作为新二叉树的节点。

返回合并后的二叉树。合并过程必须从两个树的根节点开始。

思考一:前序遍历+递归。终止条件:两二叉树均空则返回空,若一二叉树为空则直接返回另一二叉树。单层递归逻辑:创建新节点,其val值为两二叉树节点val值之和,并处理两二叉树左右子树作为新节点的左右子树。

代码:

class Solution {
public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if (!root1 && !root2)   return nullptr;if (root1 && !root2)    return root1;if (!root1 && root2)    return root2;TreeNode* root = new TreeNode(root1->val + root2->val);        //中root->left = mergeTrees(root1->left,root2->left);        //左root->right = mergeTrees(root1->right, root2->right);        //右return root;}
};

思考二:前序遍历+迭代。指定修改一二叉树返回。循环中一次性将两二叉树相同位置的结点加入队列,一次性将队列中的两个结点处理。两二叉树的孩子结点均不为空则加入队列下一个循环处理。若指定返回二叉树的孩子结点为空而另一二叉树对应孩子节点不为空则更改指针即可。

代码:

class Solution {
public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if (!root1) return root2;if (!root2) return root1;queue<TreeNode*> que;que.push(root1);que.push(root2);while (!que.empty()) {TreeNode* cur1 = que.front(); que.pop();TreeNode* cur2 = que.front(); que.pop();cur1->val += cur2->val;     //中//两二叉树左子树均不为空if (cur1->left && cur2->left) {que.push(cur1->left);que.push(cur2->left);}//两二叉树右子树均不为空if (cur1->right && cur2->right) {que.push(cur1->right);que.push(cur2->right);}//cur1左子树为空,cur2左子树不为空if (!cur1->left && cur2->left)cur1->left = cur2->left;//cur1右子树为空,cur2右子树不为空if (!cur1->right && cur2->right)cur1->right = cur2->right;                }return root1;}
};

Leetcode 700.二叉搜索树中的搜索

题目链接:700 二叉搜索树中的搜索

题干:给定二叉搜索树(BST)的根节点root和一个整数值val。你需要在 BST 中找到节点值等于val的节点。返回以该节点为根的子树。如果节点不存在,则返回null

思考一:递归法。当前节点为空则查找失败返回空,若不为空则比较val值。若当前节点val值等于目标值则返回当前节点,若大于则递归左子树,若小于则递归右子树。

代码:

class Solution {
public:TreeNode* searchBST(TreeNode* root, int val) {if (!root)  return nullptr;if (root->val == val)   return root;        //查找成功else if (root->val > val)        //当前节点值大return searchBST(root->left, val);else        //当前节点值小return searchBST(root->right, val);}
};

 思考二:迭代法。通常思路:使用栈来模拟深度遍历,使用队列来模拟广度遍历。而二叉搜索树存在节点的有序性,不使用辅助栈或者队列就可以写出迭代法。

代码:

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

Leetcode 98.验证二叉搜索树

题目链接:98 验证二叉搜索树

题干:给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效二叉搜索树定义如下:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

思考一:中序递归法。关键点:中序遍历下,输出的二叉搜索树节点的数值是有序序列。

易错点:

  • 不能单纯的比较左节点小于中间节点,右节点大于中间节点。要比较的是左子树所有节点小于中间节点,右子树所有节点大于中间节点。
  • 样例中最小节点可能是int的最小值,如果这样使用最小的int做初始值来比较也是不行的,最佳方式:记录前一个遍历的节点,初始值设为null,判断逻辑也除去null情况

中序递归遍历,记录左右子树递归返回结果,判断当前节点时遇到当前节点root的val值是否小于前一个节点pre的val值,是则返回false。判断逻辑结束后要修改前一个节点pre指向当前节点root

代码:

class Solution {
public:TreeNode* pre = nullptr;        //记录前一个结点bool isValidBST(TreeNode* root) {if (!root)  return true;bool left = isValidBST(root->left);     //左if (pre != nullptr && pre->val >= root->val)    return 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 = nullptr;        //记录前一个节点while(cur != nullptr || !st.empty()) {if (cur != nullptr) {st.push(cur);cur = cur->left;        //左    指针访问到最左下} else {cur = st.top();     //栈顶存放着左孩子为空或者左孩子已经访问过的节点st.pop();if (pre != nullptr && pre->val >= cur->val)   return false;       //中pre = cur;      //更新前一个节点cur = cur->right;   //右}}return true;}
};

思考三:统一迭代法。在中序遍历统一迭代写法的基础上简单修改循环中的处理逻辑:改为判断逻辑和更新记录节点

中序遍历统一迭代写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法

代码:

class Solution {
public:bool isValidBST(TreeNode* root) {stack<TreeNode*> st;if (root == nullptr) return true;TreeNode* pre = nullptr;        //记录前一个节点st.push(root);while (!st.empty()) {TreeNode* cur = st.top();if (cur != nullptr) {st.pop();if (cur->right != nullptr)  st.push(cur->right);        //右st.push(cur);       //中st.push(nullptr);//null标志 说明后面的节点访问过if (cur->left != nullptr)   st.push(cur->left);     //左} else {        //遇到null统一输出栈中后一个元素st.pop();cur = st.top();st.pop();if (pre != nullptr && pre->val >= cur->val) return false;pre = cur;}}return true;}
};

自我总结:

  • 理解二叉搜索树的节点有序性在代码中的体现,不涉及验证二叉搜索树性质的遍历时无需除栈和队列的辅助  
  • 再次复习二叉树三种遍历方式的迭代法和统一迭代法,回忆起循环逻辑。

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

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

相关文章

浅谈隔离放大器

浅谈隔离放大器 定义&#xff1a;隔离放大器是将输入的电量信号或物理量信号通过一种技术手段处理后,隔离输出一组模拟量信号,这组模拟量信号是以标准的4-20mA/0-20mA/0-10mA/0-10V/0-5V/1-5V/2-10V/0-2.5V/0-20mA/0-10mA/0-10V/0-100mV/0-5V等信号,以便控制系统及仪器仪表设备…

PGsql 解析json及json数组

创建测试数据 drop table if exists json_test; create table json_test as select 111 as id, {"nodes":{"1692328028076":{"nodeId":"1692328028076","nodeName":"测试表1","nodeType":"DATACO…

单片机学习笔记---定时器计数器(含寄存器)工作原理介绍(详解篇2)

目录 T1工作在方式2时 T0工作在方式3时 四种工作方式的总结 定时计数器对输入信号的要求 定时计数器对的编程的一个要求 关于初值计算的问题 4种工作方式的最大定时时间的大小 关于编程方式的问题 实例分析 实例1 实例2 T1工作在方式2时 51单片机&#xff0c;有两个…

vue实践:构建高效的电子签名功能

前言 在现代数字化时代&#xff0c;电子签名成为了一种方便、高效且安全的签署文件的方式。本文将介绍电子签名的原理和实现方法&#xff0c;帮助你快速掌握这一重要的工具。 电子签名是什么&#xff1f; 电子签名是一种数字化的签名方式&#xff0c;用于验证和确认电子文档、…

matlab appdesigner系列-app程序打包成可执行exe程序

提供了3种打包方式&#xff1a; 1&#xff09;Matlab App &#xff0c;这种方式是生成Matlab内部使用的小程序&#xff0c;可添加到matlab app菜单栏中的常用程序中&#xff0c;也就是应用该程序之前&#xff0c;你必须安装了matlab&#xff1b; 2&#xff09;Web app 3&…

vs 撤销本地 commit 并保留更改

没想到特别好的办法&#xff0c;我想的是用 vs 打开 git 命令行工具 然后通过 git 命令来撤销提交&#xff0c;尝试之前建议先建个分支实验&#xff0c;以免丢失代码&#xff0c; git 操作见 git 合并多个 commit / 修改上一次 commit

GDB调试技巧实战--自动化画出类关系图

1. 前言 上节我们在帖子《Modern C++利用工具快速理解std::tuple的实现原理》根据GDB的ptype命令快速的理解了std::tuple数据结构的实现,但是手动一个个打印,然后手动画出的UML图,这个过程明显可以自动化。 本文旨在写一个GDB python脚本把这个过程自动化。 本脚本也可以用…

2024年Java高阶面试题

2024年Java实战面试题&#xff08;北京&#xff09;_java 5 年 面试-CSDN博客 一、在面对千万条并发请求的情况下&#xff0c;如果数据库频繁查询导致崩溃&#xff0c;可以采取以下措施来解决问题: 1.缓存数据:可以使用缓存技术来减少对数据库的查询次数。将经常查询的数据存储…

2024.1.29 GNSS 学习笔记

1.假设只对4颗卫星进行观测定位&#xff0c;卫星的截止高度角是15&#xff0c;那么如何布设这四颗卫星的位置&#xff0c;使其围成的四面体的体积得到最大&#xff0c;以获得最好定位精度&#xff1f; 答&#xff1a;3颗卫星均匀分布在最低仰角面上&#xff0c;第4颗卫星在测站…

华为笔记本matebook pro X如何扩容 C 盘空间

一、前提条件 磁盘扩展与合并必须是相邻分区空间&#xff0c;且两个磁盘类型需要相同。以磁盘分区为 C 盘和 D 盘为例&#xff0c;如果您希望增加 C 盘容量&#xff0c;可以先将 D 盘合并到 C 盘&#xff0c;然后重新创建磁盘分区&#xff0c;分配 C 盘和 D 盘的空间大小。 访…

git push后,如何撤销git log上的错误注释

修改了本地的代码&#xff0c;执行了下面的操作&#xff0c;提交之后&#xff0c;怎么样修改 git add ********(文件名)//git add 添加修改文件名之后 git commit //git commit 在当前分支提交&#xff0c;编写提交注释 git push //git push 提交修…

Android T 远程动画显示流程(更新中)

序 本地动画和远程动画区别是什么? 本地动画&#xff1a;自给自足。对自身SurfaceControl矢量动画进行控制。 远程动画&#xff1a;拿来吧你&#xff01;一个app A对另一个app B通过binder跨进程通信&#xff0c;控制app B的SurfaceControl矢量动画。 无论是本地动画还是远程…

C++ Qt开发:运用QJSON模块解析数据

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QJson组件的实现对JSON文本的灵活解析…

Filter过滤器学习使用

验证token 对外API过滤器 public class APIFilter implements Filter {private static Logger logger LogManager.getLogger(APIFilter.class);private ICachedTokenService tokenService;public APIFilter(ICachedTokenService tokenService) {super();this.tokenService …

echarts:获取省、市、区/县、镇的地图数据

目录 第一章 前言 第二章 获取地图的数据&#xff08;GeoJSON格式&#xff09; 2.1 获取省、市、区/县地图数据 2.2 获取乡/镇/街道地图数据 第一章 前言 需求&#xff1a;接到要做大屏的需求&#xff0c;其中需要用echarts绘画一个地图&#xff0c;但是需要的地图是区/县…

C语言系列-整数在内存中的存储大小端字节序

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 整数在内存中的存储 大小端字节序和字节序判断 什么是大小端 为什么会有大小端 练习 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们就讲过了下面的内容 整数的2…

高端车规MCU的破局之路

目录 1 低质量的无效内卷 2 高端车规MCU产品共性 2.1 支持标定测量 2.2 低延迟通信加速 2.3 完备的网络安全解决方案 2.4虚拟化 3 国产替代的囚徒困境 1 低质量的无效内卷 近几年&#xff0c;车规MCU国产替代的呼声此消彼长&#xff0c;但仍然集中在低端产品。 从产…

JavaSE-网络编程,正则表达式

1. 网络编程 1.1 概述 Java是 Internet 上的语言&#xff0c;它从语言级上提供了对网络应用程 序的支持&#xff0c;程序员能够很容易开发常见的网络应用程序。 Java提供的网络类库&#xff0c;可以实现无痛的网络连接&#xff0c;联网的底层 细节被隐藏在 Java 的本机安装系统…

鸿蒙首批原生应用!无感验证已完美适配鸿蒙系统

顶象无感验证已成功适配鸿蒙系统&#xff0c;成为首批鸿蒙原生应用&#xff0c;助力鸿蒙生态的快速发展。 作为全场景分布式操作系统&#xff0c;鸿蒙系统旨在打破不同设备之间的界限&#xff0c;实现极速发现、极速连接、硬件互助、资源共享。迄今生态设备数已突破8亿台&…

R语言【taxlist】——tax2traits():将分类信息设置为分类单元特征

Package taxlist version 0.2.4 Description 分类法分类可以包含在taxonRelations插槽提供的信息中的 taxlist 对象中。然而&#xff0c;对于统计分析来说&#xff0c;将这些信息插入到插槽taxonTraits中可能更方便。 Usage tax2traits(object, ...)## S3 method for class …