所用代码 java
最大二叉树 LeetCode 654
题目链接:最大二叉树 LeetCode 654 - 中等
思路
感觉和昨天构建二叉树的题目一样,按我的思路有如下步骤:
- 数组长度为0,则直接返回null
- 找到数组中最大的值
- 以此值切割左子树右子树
- 左右子树递归返回根结点
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return traversal(nums);
}
public TreeNode traversal(int[] nums){
if (nums.length == 0) return null;
// 找到根结点(最大值) 与索引
int rootValue = Integer.MIN_VALUE;
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > rootValue){
rootValue = nums[i];
index = i;
}
}
// 构建根结点
TreeNode root = new TreeNode(rootValue);
// 提前返回 - 剪枝
if (nums.length == 1) return root;
// 切割左右区间
int[] leftNums = Arrays.copyOfRange(nums, 0, index);
int[] rightNums = Arrays.copyOfRange(nums, index + 1, nums.length);
// 递归的构建左右子树
root.left = traversal(leftNums);
root.right = traversal(rightNums);
return root;
}
}
还有一种不用新建数组的做法,直接通过索引构建二叉树 – 左闭右开
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return traversal(nums, 0, nums.length);
}
public TreeNode traversal(int[] nums, int begin, int end) {
if (end - begin <= 0) return null;
// 找到最大值与索引
int rootValue = Integer.MIN_VALUE;
int index = 0;
for (int i = begin; i < end; i++) {
if (nums[i] > rootValue) {
rootValue = nums[i];
index = i;
}
}
// 构建二叉树的根
TreeNode root = new TreeNode(rootValue);
// 叶子结点返回
if (end - begin == 1) return root;
// 递归构建左右子树
root.left = traversal(nums, begin, index);
root.right = traversal(nums, index + 1, end);
return root;
}
}
总结
自从学会了从中序+前序(后序)构建二叉树,这种类似的题就感觉不难了,都是同样的方法,找到根结点之后切割数组,最后递归构建左右子树就好了。
合并二叉树 LeetCode 617
题目链接:合并二叉树 LeetCode 617 - 简单
思路
无。主要是一起操作两个二叉树,同时遍历
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
return merge(root1, root2);
}
public TreeNode merge(TreeNode root1, TreeNode root2){
// root1是空结点,但是root2不是空结点的话,就把root2的结点给返回
// 若同时为null,就第一个if返回null(root2==null)
if (root1 == null) return root2;
if (root2 == null) return root1;
// 中 - 不是空结点的情况,就把值相加
TreeNode root = new TreeNode();
root.val = root1.val + root2.val;
//左 root左子树接收左边返回的结果
root.left = merge(root1.left, root2.left);
//右 root右子树接收右边返回的结果
root.right = merge(root1.right, root2.right);
return root;
}
}
可不用另外定义一个函数,我是因为习惯了,另外也可以不用新建一个二叉树,只需把原本传入的二叉树重新赋值就好了
// 中
root1.val += root2.val;
// 左
root1.left = merge(root1.left, root2.left);
// 右
root1.right = merge(root1.right, root2.right);
总结
习惯了递归时操作一个二叉树,对应同时操作两个二叉树还是不会,需要加强训练。
遍历tree1和tree的时候,时候同步进行遍历的,同时到达某一个子结点
二叉搜索树中的搜索 LeetCode 700
题目链接:二叉搜索树中的搜索 LeetCode 700 - 简单
思路
二叉搜索树是左子树小于根结点,右子树大于根结点,可以根据此特性在两端进行搜索。
递归
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
return serach(root, val);
}
public TreeNode serach(TreeNode node, int val){
if (node == null) return null;
if (node.val == val) return node;
TreeNode res = null;
if (val < node.val){
res = serach(node.left, val);
}
if (val > node.val){
res = serach(node.right, val);
}
return res;
}
}
迭代
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
// 这里使用栈和队列是一样的
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode res = null;
if (root != null) stack.push(root);
while (!stack.isEmpty()){
res = stack.pop();
if (res.val == val) return res;
else if (res.val < val && res.right != null) {
stack.push(res.right);
}
else if (res.val > val && res.left != null) {
stack.push(res.left);
}
}
// 这里不能返回res,因为没有找到时res里还存着一个结点
// 没找到直接返回null就行了
return null;
}
}
其实迭代可以不利用栈或队列存数据,代码将会更简洁
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while (root != null){
// 直接利用二叉搜索树的特性,进行左右搜索就可以了
if (root.val > val) root = root.left;
else if (root.val < val) root = root.right;
else return root;
}
return null;
}
}
总结
第一次做二叉搜索类的题目,一开始还是想的和普通二叉树方法一样,但是没必要。我们直接利用二叉搜索树的特性,可以将代码写的很简短很漂亮。
验证二叉搜索树 LeetCode 98
题目链接:验证二叉搜索树 LeetCode 98 - 中等
思路
无。
我们要利用二叉搜索树的特性:二叉树中序遍历元素一定是有序的(单调递增)
null 是二叉搜索树
class Solution {
public boolean isValidBST(TreeNode root) {
List<Integer> list = new ArrayList<>();
inorder(root, list);
for (int i = 0; i < list.size() - 1; i++) {
// 二叉搜索树不能有相同的数值
if (list.get(i) >= list.get(i+1)) return false;
}
return true;
}
public void inorder(TreeNode node, List<Integer> list){
if (node == null) return;
inorder(node.left, list);
// 中
list.add(node.val);
inorder(node.right, list);
}
}
根据上面的方法,可以得到一个更简单的,这个方法就不用额外的list去存二叉树里面的值
注意: 本题结点值的范围 -2^31 <= Node.val <= 2^31 - 1
所以不能用Integer.MIN_VALUE,因为node可能包含最小值了,所以要用long型。
class Solution {
long pre = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
return inorder(root);
}
// 中序
public boolean inorder(TreeNode node){
if (node == null) return true;
boolean left = inorder(node.left);
// 中 - 只需要判断每次加入的数值是不是比前一个大就好了(二叉搜索树性质)
if (node.val > pre){
pre = node.val;
}else return false;
boolean right = inorder(node.right);
return left && right;
}
}
但是如果有更小的数呢?我们必须避免这种情况,所以用双指针的方式进行改进。
class Solution {
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
return inorder(root);
}
// 中序
public boolean inorder(TreeNode node){
if (node == null) return true;
boolean left = inorder(node.left);
// 中 - 只需要判断每次加入的数值是不是比前一个大就好了(二叉搜索树性质)
if (pre != null && pre.val >= node.val){
return false;
}
// 当到第一个结点(最左边的叶子结点)时,不会进入if,直接把第一个结点赋值给pre
// 再此递归时,已经到了叶子结点了父节点,而pre就是他的前一个结点
pre = node;
boolean right = inorder(node.right);
return left && right;
}
}
总结
本题必须要想到二叉搜索树的性质,不然就很难,特别是中序遍历出来的结果是单调递增
这一点!