当前位置: 首页 > news >正文

DAY17||654.最大二叉树 |617.合并二叉树 |700.二叉搜索树中的搜索 |

654.最大二叉树

题目:654. 最大二叉树 - 力扣(LeetCode)

给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:

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

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

示例 :

654.最大二叉树

构造二叉树一定要前序遍历,因为要先找到中,才能分割左右子树。

本题图解思路:

 

 

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){maxValueIndex=i;maxValue=nums[i];}}node->val=maxValue;//分割左右子树//递归构建左子树,新建一个数组if(maxValueIndex>0)//保证数组元素大于1,如果不写if的话就要另开一个函数,空的情况返回{vector<int>leftvec(nums.begin(),nums.begin()+maxValueIndex);//左开右闭写法node->left=constructMaximumBinaryTree(leftvec);}//递归构建右子树if(maxValueIndex<nums.size()-1){vector<int>rightvec(nums.begin()+maxValueIndex+1,nums.end());node->right=constructMaximumBinaryTree(rightvec);}return node;}
};

本题做过上一题构建数组确实变得容易很多。代码优化方向有,不用每次找左右子树的时候构建新的数组的话效率就会高很多。(即直接用下标索引直接在原数组上操作)

关于加不加if的问题,如果允许空节点进入递归,就不加。

617.合并二叉树

题目:617. 合并二叉树 - 力扣(LeetCode)

 

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

617.合并二叉树

注意: 合并必须从两个树的根节点开始

哪种遍历方式都可以。其实就是同时遍历两颗二叉树,相同位置上的元素合并,如果一方有一方没有的情况,就把2的节点加到1中去就可以了。

前序遍历例子:

前中后序的区别只是左中右的顺序调换而已。

前序遍历(递归) 

class Solution {
public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if(root1==NULL)return root2;//如果t1为空,返回t2的值。如果两个都为空,那么其实返回的就是NULL了if(root2==NULL)return root1;root1->val+=root2->val;//合并root1->left=mergeTrees(root1->left,root2->left);root1->right=mergeTrees(root1->right,root2->right);return root1;}
};

迭代法(层序遍历) 

使用队列来其实这个写法最好理解思路。

同时遍历,入队列。

需要注意的是节点的左右子树入列的时候,当左子树不为空,右子树为空的情况其实已经被左子树的值加上右子树的值 处理过了

class Solution {
public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if(root1==NULL)return root2;if(root2==NULL)return root1;queue<TreeNode*>que;//一定都不为空入列que.push(root1);que.push(root2);while(!que.empty()){TreeNode*node1=que.front();que.pop();TreeNode*node2=que.front();que.pop();node1->val+=node2->val;//合并步骤if(node1->left&&node2->left)//两棵树的左子树都在,入列{que.push(node1->left);que.push(node2->left);}if(node1->right&&node2->right)//两棵树的右子树都在,入列{que.push(node1->right);que.push(node2->right);}if(!node1->left&&node2->left)//如果1的左空,2的不空,就把2的赋值过去{node1->left=node2->left;}if(!node1->right&&node2->right)//如果1的左空,2的不空,就把2的赋值过去{node1->right=node2->right;}}return root1;//这里返回的是root1而不是node1的原因是,node1只是一个指针,它操作的对象是入了队列的root数}
};

其实,定义的node1和node2是一个指针,它指向指针root1和root2.

700.二叉搜索树中的搜索

题目700. 二叉搜索树中的搜索 - 力扣(LeetCode) 

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

例如,

700.二叉搜索树中的搜索

在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。

 

二叉搜索树是一个有序树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉搜索树

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

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

递归法

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){if(root->val>val)root=root->left;else if(root->val<val)root=root->right;else return root;}return NULL;}
};

 本题真的非常简单。因为二叉搜索树的有序性,所以不需要回溯写法。

98.验证二叉搜索树

题目:98. 验证二叉搜索树 - 力扣(LeetCode)

本题看似简单,陷阱挺多的。

我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点

递归法

1.可以中序遍历,把所有节点元素放入一个数组,然后在逐个比较数组是否是递增的。(个人觉得好理解)

class Solution {private:vector<int>vec;void traversal(TreeNode*root){if(root==NULL)return;if(root->left)traversal(root->left);vec.push_back(root->val);if(root->right)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;}
};

2.当然也可以直接在递归遍历时比较节点大小

只有寻找某一条边(或者一个节点)的时候,递归函数会有bool类型的返回值。

其实本题是同样的道理,我们在寻找一个不符合条件的节点,如果没有找到这个节点就遍历了整个树,如果找到不符合的节点了,立刻返回。

class Solution {
public:long long maxVal = LONG_MIN; // 因为后台测试数据中有int最小值bool isValidBST(TreeNode* root) {if(root==NULL)return true;//空二叉树(即没有任何节点的二叉树)被认为是一个有效的二叉搜索树(BST)。bool right=isValidBST(root->left);//左//中。遍历整棵树,其实如果遇到不是递增的情况就返回false了。if(maxVal<root->val)maxVal=root->val;else return false;bool left=isValidBST(root->right);//右,不需要加if条件判断节点是否为空,因为空节点直接返回truereturn left&&right;}
};

maxVal 是一个用于记录当前遍历到的节点值的变量。初始化为 LONG_MIN 是为了确保无论二叉树的节点值多小,它们最初都比这个值大。这是因为二叉搜索树的性质要求,左子树中的所有节点值都小于根节点值。

最终返回值是左子树(right)和右子树(left)验证结果的 逻辑与&&),只有当左右子树都为 BST 时,整棵树才是 BST。

迭代法 

使用栈模拟中序遍历。其实就是双指针法的。一个指针指向树节点,另一个指针指向NULL。当中序遍历走的过程,两指针同步移动,pre一定指向cur的上一个指向的节点。(好理解)

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);cur=cur->left;//左子树入栈,按栈的存储结构和中序遍历特性,只要左树一直不为空,就入栈,反正出栈顺序就是中序遍历的顺序!}else//如果当前节点为空但栈不为空,就遍历右子树,并且比较是否符合二叉搜索树特性{cur=st.top();//中st.pop();//出栈if(pre!=NULL&&cur->val<=pre->val)return false;//错误情况pre=cur;//保存上一个数cur=cur->right;//右}}return true;  }
};

相关文章:

  • 设计模式之享元(Flyweight)模式
  • mac-m1安装nvm,docker,miniconda
  • 基于SpringBoot+Vue的小儿推拿培训管理系统
  • [SAP ABAP] SELECTION-SCREEN
  • mac 外接键盘
  • 手机/平板端 Wallpaper 动态壁纸文件获取及白嫖使用指南
  • AURIX单片机示例:开发入门与点亮LED
  • 2015年国赛高教杯数学建模B题互联网+时代的出租车资源配置解题全过程文档及程序
  • Ansible-template模块动态生成特定文件
  • docker常用命令、如何查看docker 镜像的sha256值
  • TCP网络编程概述、相关函数、及实现超详解
  • 【frp】frp重启、frp启动、frp后台启动、frps dashboard等等
  • Linux网络:网络编程套接字
  • 【web网页制作】html+css旅游家乡河南开封主题网页制作(4页面)【附源码】
  • Linux基础命令cd详解
  • 【347天】每日项目总结系列085(2018.01.18)
  • Docker: 容器互访的三种方式
  • in typeof instanceof ===这些运算符有什么作用
  • laravel with 查询列表限制条数
  • leetcode讲解--894. All Possible Full Binary Trees
  • mac修复ab及siege安装
  • ReactNative开发常用的三方模块
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • Spring Cloud Alibaba迁移指南(一):一行代码从 Hystrix 迁移到 Sentinel
  • 成为一名优秀的Developer的书单
  • 大主子表关联的性能优化方法
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 欢迎参加第二届中国游戏开发者大会
  • 深入浅出webpack学习(1)--核心概念
  • 我的面试准备过程--容器(更新中)
  • 一个完整Java Web项目背后的密码
  • 智能网联汽车信息安全
  • Nginx实现动静分离
  • 阿里云ACE认证之理解CDN技术
  • ​​​​​​​ubuntu16.04 fastreid训练过程
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Redis使用系列) Springboot 使用Redis+Session实现Session共享 ,简单的单点登录 五
  • (Ruby)Ubuntu12.04安装Rails环境
  • (十)DDRC架构组成、效率Efficiency及功能实现
  • (游戏设计草稿) 《外卖员模拟器》 (3D 科幻 角色扮演 开放世界 AI VR)
  • (转)IIS6 ASP 0251超过响应缓冲区限制错误的解决方法
  • (转载)Linux网络编程入门
  • .Net - 类的介绍
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .net core IResultFilter 的 OnResultExecuted和OnResultExecuting的区别
  • .NET Core 成都线下面基会拉开序幕
  • .NET Core日志内容详解,详解不同日志级别的区别和有关日志记录的实用工具和第三方库详解与示例
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 药厂业务系统 CPU爆高分析
  • .Net 执行Linux下多行shell命令方法
  • .NET/C# 使窗口永不获得焦点
  • .Net接口调试与案例
  • .NET开源项目介绍及资源推荐:数据持久层
  • @EnableWebSecurity 注解的用途及适用场景