代码随想录训练营D20-二叉树篇 p6 | 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
(一) 654.最大二叉树
又是构造二叉树,昨天大家刚刚做完 中序后序确定二叉树,今天做这个 应该会容易一些, 先看视频,好好体会一下 为什么构造二叉树都是 前序遍历
1. 思路
递归
1)参数、返回值
参数:传入待排序的数组
返回值:返回当前这棵树的根结点。
2)终止条件
题目中给出,树结点数量至少为1,所以终止条件是 数组大小为1时,则当前结点就是未来的叶子结点,直接以该值创建一个节点并返回。
3)单层递归逻辑
以前序遍历创建树,先创建根节点,再为该根节点创建左右孩子。
- 选出当前数组中的最大值,并记录其index下标
- 使用该值创建一个树结点node。
- 以该index位置,将数组划分左右两部分。
- 递归左右两部分,并返回给node作为其左右子树。递归左右之前先要判断左右区间中至少有一个元素。!!
返回node。
1.1 直观思路
1.2 代码随想录思路
2. 代码
public static void main(String[] args) {
Solution solution = new Q654ConstructMaximumBinaryTree().new Solution();
TreeNode node = solution.constructMaximumBinaryTree(new int[]{3, 2, 1, 6, 0, 5});
System.out.println();
}
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return buildHelper(nums, 0, nums.length);//这里注意 左闭右开,左右右侧到nums.length
}
private TreeNode buildHelper(int[] nums, int left, int right){
//判空
if(right <= left){
return null;
}
//终止条件
if(nums.length == 1){
return new TreeNode(nums[0]);
}
//单层递归逻辑 前序遍历创建树
//选出数组中的最大值,并记录下标index
int index = 0;
int maxValue = Integer.MIN_VALUE;
for(int i = left; i < right; ++i){//注意这里i的范围 不是 0, nums.length
if(nums[i] > maxValue){
maxValue = nums[i];
index = i;
}
}
//System.out.println("index " + index + ", maxvalue " + maxValue);
//创建节点
TreeNode node = new TreeNode(maxValue);
//切割数组
int leftStart = left;
int leftEnd = index;
int rightStart = index + 1;
int rightEnd = right;
System.out.println("left [" + leftStart + "," + leftEnd + ")");
System.out.println("left [" + rightStart + "," + rightEnd + ")");
node.left = buildHelper(nums, leftStart, leftEnd);
node.right = buildHelper(nums, rightStart, rightEnd);
return node;
}
}
3. 总结
1.判空时,何时以数组中没有元素为判断条件?何时以数组中只有一个元素为判断条件?
力扣题目中给出,树中至少有一个结点。则判空的判断条件是,数组中只有一个元素时。
力扣题目给出,树中可能有0个结点。则判空的判断条件是,数组中没有结点时。
2.所有涉及区间的地方都很容易出错。
(二) 617.合并二叉树
这次是一起操作两个二叉树了, 估计大家也没一起操作过两个二叉树,也不知道该如何一起操作,可以看视频先理解一下。 优先掌握递归。
1. 思路
使用tree1的整体结构,tree1没有结点的位置直接使用tree2结点。
递归
1)参数:两个树的根结点;返回值:tree1的根结点。因为新树的根结点就是tree1的根结点。
2)递归终止条件。tree1结点为null,返回tree2(直接使用tree2的结点,此时tree2为null也没关系,返回合并后为null)。tree2的结点为null,返回tree1(此时tree1为null也没关系,返回合并后为null)。
3)单层递归逻辑。
对于构建二叉树,较直观的思路是前序遍历,根左右。
经过上面的递归终止条件,现在的tree1、tree2都不为null。
- 根。tree1节点值 += tree2节点值
- 递归处理左子树
- 递归处理右子树
返回这个根。
2. 代码
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
//1.递归终止条件。题中说可能结点数为0,所以判空。
if(root1 == null){
return root2;
}
if(root2 == null){
return root1;
}
//root1 root2 都不为null
//2.单层递归逻辑
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
3. 学习到的东西
一个判空,也是递归终止条件。
这样包括了三种情形:都为null、其中只有一个为null
if(root1 == null){
return root2;
}
if(root2 == null){
return root1;
}
(三) 700.二叉搜索树中的搜索
递归和迭代 都可以掌握以下,因为本题比较简单, 了解一下 二叉搜索树的特性
1. 思路
【1.递归】
1.返回值和参数
参数:根结点、目标值。返回值:该目标值所存在的树 的根结点。
2.终止条件
因为本题说明,至少有一个结点。所以终止条件:判断是否是叶子结点,若是叶子结点,判断其值与目标值是否相等,若相等,直接返回该节点;否则,返回null(表示该树中没有该值)
恭喜!上面是错的哈哈哈哈。
终止条件还得是,若为空节点,返回null。
3.单层递归逻辑-前序遍历
根。目标值与当前结点值相等,则直接 return
递归查找左右子树。
若最终都无,return null。
【2.迭代】
使用层序遍历,借助队列。
2. 代码
【1.递归】
public TreeNode searchBST(TreeNode root, int val) {
//1.终止条件。是否是空结点
if(root == null){
return null;
}
if(root.val == val){
return root;
}else if(root.val > val){
return searchBST(root.left, val);
}else {
return searchBST(root.right, val);
}
}
【2.递归-优化】
将判空 和 目标值与当前结点值相等,在一起写了。
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) {
return root;
}
if (val < root.val) {
return searchBST(root.left, val);
} else {
return searchBST(root.right, val);
}
}
【3.迭代】
//2.迭代
public TreeNode searchBST1(TreeNode root, int val) {
//不判空 因为题目说了 至少有一个结点
//1.创建队列
Deque<TreeNode> queue = new ArrayDeque();
queue.add(root);
//2.开启外层循环,条件是队列不为空
while(!queue.isEmpty()){
//本题不需要区分每层,所以不需要内层循环
TreeNode node = queue.poll();
//开启判断,若该结点为查找的结点,则return
if(node.val == val){
return node;
}
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
return null;
}
(四) 98.验证二叉搜索树
遇到 搜索树,一定想着中序遍历,这样才能利用上特性。
但本题是有陷阱的,可以自己先做一做,然后在看题解,看看自己是不是掉陷阱里了。这样理解的更深刻。
1. 思路
建议使用中序遍历。因为搜索二叉树的中序遍历是严格递增的,所以设置一个变量存当前已遍历到的最大值max,在遍历时,若有值小于max,则false;
1.2 代码随想录思路
二叉搜索树可以为空,所以终止条件是,若为null,则返回true。
法1:使用long中最小值作为起始值,因为测试数据中会用到int的最小值。
法2:只用当前结点值 与 前一个节点值相比较的方法。
若当前值 比 前一个值大,则返回true,否则false
2. 代码
法1:使用Long.MIN 作为起始值
public boolean isValidBST(TreeNode root) {
return isValid(root);
}
private long pre = Long.MIN_VALUE;
private boolean isValid(TreeNode node){
//终止条件
if(node == null){
return true;
}
//中序遍历
boolean left = isValidBST(node.left);
if(node.val > pre){
pre = node.val;
}else {
return false;
}
boolean right = isValidBST(node.right);
return left && right;
}
法2(推荐):创建一个pre节点,用于与当前结点比较数值
public boolean isValidBST(TreeNode root) {
return isValid(root);
}
//法2
private TreeNode preNode = null;
private boolean isValid(TreeNode node){
if(node == null){
return true;
}
boolean left = isValid(node.left);
if(preNode != null && preNode.val >= node.val){
return false;
}
preNode = node;
boolean right = isValid(node.right);
return left && right;
}
3.实现过程中的问题
写递归,脑子就错乱。问就是在梦游。
public boolean isValidBST(TreeNode root) {
return isValid(root, Integer.MIN_VALUE);
}
private boolean isValid(TreeNode node, int value){
if(node.left == null && node.right == null){
if(node.val > value){
value = node.val;
return true;
}else {
return false;
}
}
if(node.left != null){
boolean left = isValid(node.left, value);
if(!left){
return false;
}else{
value = node.left.val;
}
}
if(node.val <= value){
return false;
}else{
value = node.val;
}
if(node.right != null){
boolean right = isValid(node.right, value);
if(!right){
return false;
}else{
value = node.right.val;
}
}
return true;
}
4. 总结
二叉搜索树的中序遍历严格递增的,要记得使用中序遍历体现其特性。
(五) 今日收获,记录一下自己的学习时长
题目类型:构造二叉树。都是使用先序遍历,先构造根节点,再为根节点添加左结点、右结点。