654.最大二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树
654.最大二叉树
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
- 二叉树的根是数组中的最大元素。
- 左子树是通过数组中最大值左边部分构造出的最大二叉树。
- 右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
示例 :
提示:
给定的数组的大小在 [1, 1000] 之间。
思路
递归法 前序遍历
思路和【106.从中序与后序遍历序列构造二叉树】很类似
首先遍历数组找到最大值,保存下标Index
依据下标,分割数组为左数组和有数组
递归法三要素
1.方法入参和返回值 入参:int[] nums 返回值TreeNode node
2.终止条件 数组为空
3.核心逻辑 前序遍历,因为先构造中间节点,然后递归构造左子树和右子树
代码如下
// 时间复杂度O(n) 构建二叉树需要遍历数组所有结点
// 空间复杂度O(n) 构建的二叉树深度最大为n
public TreeNode constructMaximumBinaryTree(int[] nums) {
if(nums == null){
return null;
}
return travel(nums);
}
public TreeNode travel(int[] nums) {
if (nums.length == 0)
return null;
int rootVal = Integer.MIN_VALUE;
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] > rootVal) {
rootVal = nums[i];
index = i;
}
}
int[] leftNums = new int[index];
int[] rightNums = new int[nums.length - index - 1];
for (int i = 0; i < index; i++) {
leftNums[i] = nums[i];
}
for (int i = index + 1; i < nums.length; i++) {
rightNums[i - index - 1] = nums[i];
}
TreeNode root = new TreeNode(rootVal,null,null);
root.left = travel(leftNums);
root.right = travel(rightNums);
return root;
}
617.合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
注意: 合并必须从两个树的根节点开始。
思路
先序遍历 递归法
因为需要先访问根节点,然后一层层访问到叶子结点,所以先序遍历符合条件
使用递归法,同时对两个树的左子树和右子树进行递归。这里就用两个树的左子树为例
1.如果A,B两个结点不等于空,那么A,B两树的左子树同时加入递归循环。A,B存在空结点的话,传递null值代替空结点
2.将A,B两树的左子树val之和赋予新结点。
递归法三要素
1.方法入参和返回值 入参:TreeNode root1, TreeNode root2 返回值:TreeNode root
2.中止条件 :两个树都遍历到叶子结点
3.核心逻辑 先序遍历
代码如下
// 时间复杂度O(N)
// 空间复杂度o(n)
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
return travel(root1, root2);
}
public TreeNode travel(TreeNode root1, TreeNode root2) {
if (root1 == null && root2 == null) {// 中止条件 :两个树都遍历到叶子结点
return null;
}
int rootVal = 0;
if (root1 != null)
rootVal = rootVal + root1.val;
if (root2 != null)
rootVal = rootVal + root2.val;
TreeNode root = new TreeNode(rootVal, null, null);
if (root1 != null && root2 != null) {
root.left = mergeTrees(root1.left, root2.left);
root.right = mergeTrees(root1.right, root2.right);
} else if (root1 != null && root2 == null) {
root.left = mergeTrees(root1.left, null);
root.right = mergeTrees(root1.right, null);
} else if (root1 == null && root2 != null) {
root.left = mergeTrees(null, root2.left);
root.right = mergeTrees(null, root2.right);
}
return root;
}
700.二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
例如,
在上述示例中,如果要找的值是 5,但因为没有节点值为 5,我们应该返回 NULL。
思路
前序遍历 递归
二叉搜索树的特性是 左子树结点小于根节点,右子树结点大于根节点
利用特性,先跟根节点比较,然后在根据比较结果,跟左子树或右子树比较
递归三要素
1.方法入参和返回值 入参:TreeNode root int val 返回值 TreeNode node
2.中止条件 遍历到叶子结点 或者 遇到结点值等于val
3.核心逻辑 前序遍历 要先访问根节点,然后访问左右子树,前序遍历满足条件
代码如下
// 时间复杂度o(n)
// 空间复杂度o(n)
public TreeNode searchBST(TreeNode root, int val) {
if (root == null)
return null;
return travel(root, val);
}
public TreeNode travel(TreeNode root, int val) {
if (root.val == val) {// 中止条件 遍历到叶子结点 或者 遇到结点值等于val
return root;
}
if (root.right == null && root.left == null) {
return null;
}
TreeNode node = null;
if (root.val > val && root.left != null) {// 核心逻辑 前序遍历 要先访问根节点,然后访问左右子树,前序遍历满足条件
node = travel(root.left, val);
}else if (root.val < val && root.right != null) {
node = travel(root.right, val);
}
return node;
}
98.验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思路:递归法
将二叉搜索树中序遍历,输出的结果是升序排序的
那么先进行中序遍历,判断中序遍历结果是否为升序即可
代码如下
// 时间复杂度o(n)
// 空间复杂度o(n)
public boolean isValidBST(TreeNode root) {
if (root == null)
return true;
List<Integer> result = new ArrayList<>();
travel(root, result);
for (int i = 0; i < result.size() - 1; i++) {
if (result.get(i) >= result.get(i + 1))
return false;
}
return true;
}
// 中序遍历
public void travel(TreeNode root, List<Integer> result) {
if (root == null) {
return;
}
travel(root.left, result);
result.add(root.val);
travel(root.right, result);
}
思路:递归法(更优解)
在递归的过程中判断中序遍历的顺序,效率比中序遍历结束后判断数组顺序要高
观察中序遍历的代码:
1.中序遍历的结果由【result.add(root.val)】 加入
2.那么保存root.val,在下次调用result.add(root.val)时,比较顺序。
3.即可在二叉树遍历过程中,比较结点大小public void travel(TreeNode root, List<Integer> result) { if (root == null) { return; } travel(root.left, result);// 左子树 result.add(root.val); travel(root.right, result);// 右子树 }
代码如下
// 时间复杂度o(n)
// 空间复杂度o(n)
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null)
return true;
return travel(root);
}
// 中序遍历
public boolean travel(TreeNode root) {
if (root == null) {
return true;
}
if (!travel(root.left))
return false;
if (pre != null && pre.val >= root.val) {
return false;
}
pre = root;
if (!travel(root.right))
return false;
return true;
}
思路:迭代法
在中序遍历迭代法的基础上做改动
新增preNode记录中序遍历的前一个结点
前一个结点和当前结点作比较即可
代码如下
// 时间复杂度o(n)
// 空间复杂度o(n)
TreeNode preNode = null;
public Boolean inorderTraversal1(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (!stack.isEmpty() || cur != null) {// 当指针元素为空 并且 栈为空时,结束循环
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
if (preNode != null && preNode.val >= cur.val)
return false;
preNode = cur;
cur = cur.right;
}
}
return true;
}