代码随想录刷题day17丨654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树
代码随想录刷题day17丨654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树
1.题目
1.1最大二叉树
-
题目链接:654. 最大二叉树 - 力扣(LeetCode)
-
视频讲解:又是构造二叉树,又有很多坑!| LeetCode:654.最大二叉树_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0654.%E6%9C%80%E5%A4%A7%E4%BA%8C%E5%8F%89%E6%A0%91.html
-
解题思路:递归(前序遍历)
- 构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
- 确定递归函数的参数和返回值
- 参数传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。
- 确定终止条件
- 题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。
- 那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。
- 确定单层递归的逻辑
- 先要找到数组中最大的值和对应的下标, 最大的值构造根节点,下标用来下一步分割数组。
- 最大值所在的下标左区间 构造左子树(这里要判断maxValueIndex > 0,因为要保证左区间至少有一个数值。)
- 最大值所在的下标右区间 构造右子树(判断maxValueIndex < (nums.size() - 1),确保右区间至少有一个数值。)
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public TreeNode constructMaximumBinaryTree(int[] nums) { if(nums.length == 1){ return new TreeNode(nums[0]); } int maxValue = 0; int maxValueIndex = 0; for(int i =0;i < nums.length;i++){ if(nums[i] > maxValue){ maxValue = nums[i]; maxValueIndex = i; } } TreeNode node = new TreeNode(maxValue); if(maxValueIndex > 0){ int[] arrLeft = new int[maxValueIndex]; for(int i = 0;i < maxValueIndex;i++){ arrLeft[i] = nums[i]; } node.left = constructMaximumBinaryTree(arrLeft); } if(maxValueIndex < nums.length -1){ int[] arrRight = new int[nums.length - maxValueIndex - 1]; for(int i = maxValueIndex + 1;i < nums.length;i++){ arrRight[i -maxValueIndex - 1] = nums[i]; } node.right = constructMaximumBinaryTree(arrRight); } return node; } }
-
总结:
- 构造二叉树都是 前序遍历
1.2合并二叉树
-
题目链接:617. 合并二叉树 - 力扣(LeetCode)
-
视频讲解:一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0617.%E5%90%88%E5%B9%B6%E4%BA%8C%E5%8F%89%E6%A0%91.html
-
解题思路:递归(前序遍历)
- 其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。
- 确定递归函数的参数和返回值:
- 首先要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
- 确定终止条件:
- 因为是传入了两个树,那么就有两个树遍历的节点t1 和 t2,如果t1 == NULL 了,两个树合并就应该是 t2 了(如果t2也为NULL也无所谓,合并之后就是NULL)。
- 反过来如果t2 == NULL,那么两个数合并就是t1(如果t1也为NULL也无所谓,合并之后就是NULL)。
- 确定单层递归的逻辑:
- 单层递归的逻辑就比较好写了,这里我们重复利用一下t1这个树,t1就是合并之后树的根节点(就是修改了原来树的结构)。
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { if(root1 == null){ return root2; } 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; } }
-
总结:
- 优先掌握递归
- 本题递归用中序和后序也都可以,换个顺序就行,前序更方便直观理解
1.3二叉搜索树中的搜索
-
题目链接:700. 二叉搜索树中的搜索 - 力扣(LeetCode)
-
视频讲解:不愧是搜索树,这次搜索有方向了!| LeetCode:700.二叉搜索树中的搜索_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0700.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E6%90%9C%E7%B4%A2.html
-
解题思路:递归法或者迭代法
- 确定递归函数的参数和返回值
- 递归函数的参数传入的就是根节点和要搜索的数值,返回的就是以这个搜索数值所在的节点。
- 确定终止条件
- 如果root为空,或者找到这个数值了,就返回root节点。
- 确定单层递归的逻辑
- 因为二叉搜索树的节点是有序的,所以可以有方向的去搜索。
- 如果root->val > val,搜索左子树,如果root->val < val,就搜索右子树,最后如果都没有搜索到,就返回NULL。
- 确定递归函数的参数和返回值
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { public TreeNode searchBST(TreeNode root, int val) { if(root == null || root.val == val){ return root; } TreeNode resNote = null; if(val < root.val){ resNote = searchBST(root.left,val); } if(val > root.val){ resNote = searchBST(root.right,val); } return resNote; } }
class Solution { // 迭代,普通二叉树 public TreeNode searchBST(TreeNode root, int val) { if (root == null || root.val == val) { return root; } Stack<TreeNode> stack = new Stack<>(); stack.push(root); while (!stack.isEmpty()) { TreeNode pop = stack.pop(); if (pop.val == val) { return pop; } if (pop.right != null) { stack.push(pop.right); } if (pop.left != null) { stack.push(pop.left); } } return null; } } class Solution { // 迭代,利用二叉搜索树特点,优化,可以不需要栈 public TreeNode searchBST(TreeNode root, int val) { while (root != null) if (val < root.val) root = root.left; else if (val > root.val) root = root.right; else return root; return null; } }
-
总结:
- 递归和迭代 都可以掌握一下,因为本题比较简单
1.4验证二叉搜索树
-
题目链接:98. 验证二叉搜索树 - 力扣(LeetCode)
-
视频讲解:你对二叉搜索树了解的还不够! | LeetCode:98.验证二叉搜索树_哔哩哔哩_bilibili
-
文档讲解:https://programmercarl.com/0098.%E9%AA%8C%E8%AF%81%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html
-
解题思路:递归(中序遍历)
- 中序遍历看是否单调递增
- 要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
- 用双指针思想优化递归过程做该题效果最好
-
代码:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode() {} * TreeNode(int val) { this.val = val; } * TreeNode(int val, TreeNode left, TreeNode right) { * this.val = val; * this.left = left; * this.right = right; * } * } */ class Solution { TreeNode pre = null; // 类的成员变量,用来保存中序遍历过程中前一个节点 public boolean isValidBST(TreeNode root) { if(root == null){ return true;// 空树是有效的二叉搜索树 } boolean left = isValidBST(root.left); if(pre != null && pre.val >= root.val){ return false; } pre = root;// 更新 pre 为当前节点 boolean right = isValidBST(root.right); return left && right;// 返回左子树和右子树是否都是有效的 BST } }
-
总结:
- 遇到 搜索树,一定想着中序遍历,这样才能利用上特性。
- 但本题是有陷阱的
- 不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了。
- 我们要比较的是 左子树所有节点小于中间节点,右子树所有节点大于中间节点。