Leecode 654. 最大二叉树

题目描述

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

创建一个根节点,其值为 nums 中的最大值。

递归地在最大值 左边 的 子数组前缀上 构建左子树。

递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 。

  • 示例 1:



输入:nums = [3,2,1,6,0,5]

输出:[6,3,5,null,2,0,null,null,1]

  • 示例 2:

输入:nums = [3,2,1]

输出:[3,null,2,null,1]

递归建立最大二叉树

本题和昨天做过的构建二叉树的题目很像,甚至相比之下会更简单,构造树所需传入的数组只有一个。可以大致写出迭代算法的思路如下:

  • 首先确定终止条件:当前数组为空时,则返回空指针
  • 处理当前节点:找出数组中最大值的所在序号,根据最大值建立新节点
  • 递归处理左右子节点
  • 返回当前节点

根据上面思路可以得到代码如下:

class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if(nums.empty()) return nullptr; // 如果当前数组为空,则直接返回空指针
int rootIndex = 0; // 初始化根节点序号
for(int i = 0; i < nums.size(); i++){ // 遍历查找数组中最大值的序号,作为根节点的值
if(nums[i] > nums[rootIndex]) rootIndex = i;
}
TreeNode* root = new TreeNode(nums[rootIndex]); // 新建节点,用最大数建立根节点
vector<int> leftVec(nums.begin(), nums.begin() + rootIndex); // 将最大数左侧的数组作为新的数组,并递归建立左子树
root->left = constructMaximumBinaryTree(leftVec); vector<int> rightVec(nums.begin()+rootIndex+1, nums.end()); // 将最大数右侧的数组作为新的数组,并递归建立右子树
root->right = constructMaximumBinaryTree(rightVec); return root; // 返回根节点
}
};

Leecode 617. 合并二叉树

题目描述

给你两棵二叉树: root1root2

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

返回合并后的二叉树。

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

  • 示例 1:



输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]

输出:[3,4,5,5,4,null,7]

  • 示例 2:

输入:root1 = [1], root2 = [1,2]

输出:[2,2]

递归合并二叉树

本题使用递归进行合并,重点在于讨论终止条件:

  • 如果当前两棵树的当前节点都为空,则返回空
  • 如果有一棵树的当前节点为空,则直接返回另一棵树的根节点;后续子节点也相当于直接嫁接过来
  • 如果都不为空,则当前节点为两棵树的节点值相加。并需要对当前节点的左右子节点进行递归
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if(!root1 && !root2 )return nullptr; // 都为空返回空
if(!root1) return root2; // 一个为空,直接嫁接另一棵树
if(!root2) return root1;
TreeNode* root = new TreeNode(root1->val + root2->val); // 都不为空,则需要将两棵树当前节点相加
root->left = mergeTrees(root1->left, root2->left); // 递归合并左右子树
root->right = mergeTrees(root1->right, root2->right);
return root;
}
};

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

题目描述

给定二叉搜索树(BST)的根节点 root 和一个整数值 val

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null

  • 示例 1:



输入:root = [4,2,7,1,3], val = 2

输出:[2,1,3]

  • 示例 2:



输入:root = [4,2,7,1,3], val = 5

输出:[]

递归法进行查找

在二叉树中进行查找非常方便快捷,只需要从根节点开始,如果值比当前节点更大;则往右子树查找,如果值更小则往左子树查找;但如果一直找到空节点都没有相等,则返回空。可以写出代码如下:

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

Leecode 98. 验证二叉搜索树

题目描述

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

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

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

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

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

  • 示例 1:



输入:root = [2,1,3]

输出:true

  • 示例 2:



输入:root = [5,1,4,null,null,3,6]

输出:false

解释:根节点的值是 5 ,但是右子节点的值是 4

使用递归进行验证

对于本题,首先需要明确二叉搜索树的定义,对于每一个节点,都必须满足比左子树中最大的节点更大,比右子树中最小的节点更小。需要特别注意的是,这里提到的最大和最小的节点,都并不一定直接是左右子节点,在不清楚二叉搜索树的定义的情况下,非常容易混淆这一点。如果仅仅只考虑每一个节点比左子节点大,比右子节点小,那么很容易写成下面这样:

// 错误示范
class Solution {
public:
void isValidHelper(TreeNode* root, bool& isValid){
if(!root || !isValid) return;
if(root->left) {
if(root->left->val >= root->val){ // 仅与左子节点比较,正确应当是与左子树中的最大节点比较
isValid = false;
return;
}
isValidHelper(root->left, isValid);
}
if(root->right) {
if(root->right->val <= root->val){ // 仅与右子节点比较,正确应与右子树中的最小节点比较
isValid = false;
return;
}
isValidHelper(root->right,isValid);
}
return;
} bool isValidBST(TreeNode* root) {
bool isValid = true;
isValidHelper(root, isValid);
return isValid;
}
};

上面代码无法通过测试算例,而把这段代码写下来也是为了给自己加深印象(因为我第一反应就是这样写的)。

正确的思路应当是采用中序遍历,实际上对于二叉搜索树的中序遍历,正好就是排序好的序列。所以只需要记录该二叉数的中序遍历序列,再判断序列是否满足单调递增性,即可判断该序列是否为二叉搜索树。但是,如果采用先建立中序遍历,后续再遍历一遍中序遍历是否满足单调递增性,所消耗的时间复杂度:中序遍历$O(\log n) + \(遍历数组\)O(n)\(,故总时间复杂度为\)O(n)\(。而如果可以在进行中序遍历的同时就能判断是否为二叉搜索树,就可以省略后续时间复杂度为\)O(n)$的遍历。因此我们可以得到代码如下:

class Solution {
public:
void inOrder(TreeNode* curNode, vector<int>& orderVec, bool& result){ // 辅助函数,进行中序遍历,同时也要判断是否满足二叉搜索树
if(!curNode || !result) return; // 如果当前节点为空,或者已经确定不是二叉搜索树,直接返回
if(curNode->left) inOrder(curNode->left, orderVec, result); // 如果左子节点存在,则对左子节点递归调用
if(orderVec.size() > 0 && orderVec[orderVec.size()-1] >= curNode->val) { // 将当前节点与于已经记录的中序序列最后一个元素进行比较;如果记录为空,则不比较
result = false; // 如果当前节点不大于左子树中最大节点,说明不是二叉搜索树,将result设置为false
return;
}
orderVec.push_back(curNode->val); // 如果当前节点更大,则将当前节点记录到vector中
if(curNode->right) inOrder(curNode->right, orderVec, result); // 对右子树递归调用
return;
} bool isValidBST(TreeNode* root) {
bool result = true; // 初始化result为真,后续判断如果不满足再变为false,如果遍历结束都还满足则为真
vector<int> orderVec; // 初始化空的Vector,用于存放
inOrder(root, orderVec, result); // 中序遍历,同时判断是否满足二叉搜索树的条件
return result; // 返回结果
}
};

上面判断代码的时间复杂度为\(O(n)\)。

今日总结

今天题目都比较简单,加深了对于二叉搜索树的定义的熟悉。

今天力扣刷到64题了,再接再厉!

代码随想录第十七天 | Leecode 654. 最大二叉树、617. 合并二叉树、700. 二叉搜索树中的搜索、98. 验证二叉搜索树的更多相关文章

  1. 代码随想录算法训练营day20 | leetcode ● 654.最大二叉树 ● 617.合并二叉树 ● 700.二叉搜索树中的搜索 ● 98.验证二叉搜索树

    LeetCode 654.最大二叉树 分析1.0 if(start == end) return节点索引 locateMaxNode(arr,start,end) new root = 最大索引对应节 ...

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

    LeetCode 235. 二叉搜索树的最近公共祖先 分析1.0  二叉搜索树根节点元素值大小介于子树之间,所以只要找到第一个介于他俩之间的节点就行 class Solution { public T ...

  3. 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素

    第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...

  4. 代码随想录第八天 |344.反转字符串 、541. 反转字符串II、剑指Offer 05.替换空格 、151.翻转字符串里的单词 、剑指Offer58-II.左旋转字符串

    第一题344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这 ...

  5. 代码随想录-day1

    链表 今天主要是把链表专题刷完了,链表专题的题目不是很难,基本都是考察对链表的操作的理解. 在处理链表问题的时候,我们通常会引入一个哨兵节点(dummy),dummy节点指向原链表的头结点.这样,当我 ...

  6. 代码随想录 day0 博客怎么写

    前言 2.25日开始记录自己的博客生涯以及代码随想录训练营的每日内容 一.题目链接怎么找?怎么设置连接? 力扣题目链接1:力扣 二.正文怎么写? 二分查找 算法思路: 二分查找需要保证数组为有序数组同 ...

  7. 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)

    背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...

  8. 代码随想录算法训练营day13

    基础知识 二叉树基础知识 二叉树多考察完全二叉树.满二叉树,可以分为链式存储和数组存储,父子兄弟访问方式也有所不同,遍历也分为了前中后序遍历和层次遍历 Java定义 public class Tree ...

  9. leecode刷题(30)-- 二叉树的后序遍历

    leecode刷题(30)-- 二叉树的后序遍历 二叉树的后序遍历 给定一个二叉树,返回它的 后序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [3,2,1] 思路 ...

  10. leecode刷题(29)-- 二叉树的中序遍历

    leecode刷题(29)-- 二叉树的中序遍历 二叉树的中序遍历 给定一个二叉树,返回它的中序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 思路 跟 ...

随机推荐

  1. 同步工具-SeaTunnel使用

    一.介绍 SeaTunnel 是一个非常好用.超高性能的分布式数据集成平台,架构于 Apache Spark 和 Apache Flink 之上,实现了海量数据的实时同步与转换.每天可以稳定高效地同步 ...

  2. 动手学深度学习-python基础知识介绍part1

    基础详解-part1 import torch x=torch.arange(12) x x.shape x.numel() #数组中元素的总数 # 修改形状 x.reshape(3,4) torch ...

  3. 傻妞PLUS机器人教程——安装

    特性 简单易用的消息搬运功能. 简单强大的自定义回复功能. 完整支持 ECMAScript 5.1 的插件系统,基于 otto. 支持通过内置的阉割版 Express / fetch ,接入互联网. ...

  4. 批量上传序列到Gisaid数据库

    本文演示的是用Gisaid官网自带的Excel宏批量上传毒株信息和序列到Gisaid数据库,需要魔法. 1. 下载GISAID Batch Uploader 找到自己需要上传的病毒类型,我是Flu,在 ...

  5. Linux - 基础环境检查

    检查操作系统:建议根据实际产品需要进行安装 检查主机名:集群中统一前缀并区分服务器功能,小写命名 检查内存:建议至少128G 检查CPU:建议至少2个支持超线程技术的10核芯片 检查磁盘:同一功能的服 ...

  6. JS中的0和php中的0

    请注意:包含 0 的字符串 "0" 是 true 一些编程语言(比如 PHP)视 "0" 为 false.但在 JavaScript 中,非空的字符串总是 tr ...

  7. 【记录】C++STL容器/特有类 使用积累

    STL容器共有成员函数 size() max_size() empty() begin() end() clear() [链接]csdn_STL中所有容器共有成员函数 双端队列deque 1.创建与初 ...

  8. Mysql join算法深入浅出

    导语 联表查询在日常的数据库设计中非常的常见,但是联表查询可能会带来性能问题,为了调优.避免设计出有性能问题的SQL,在explain命令中,会显示用的是哪个join算法,学习一下join过程是非常有 ...

  9. mac zsh: command not found: python

    前言 在 mac 上安装 python 后,会自动在 .zprofile 文件中,加入: # Setting PATH for Python 3.12 # The original version i ...

  10. VMware ESXi系统

    esxi全称"VMware ESXi",是可直接安装在物理服务器上的强大的裸机管理系统,是一款虚拟软件,不需安装其他操作系统,是VMware服务器虚拟化的基础.通过直接访问并控制底 ...