目录
【654.最大二叉树】中等题
思路:关键 - > 单层递归逻辑
查找指定区间(左闭右开)内的最大值对应的索引,构造根节点,根据最大值索引划分左子树和右子树区间(左闭右开),构造根节点的左子树和右子树
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return getMaXTree(nums, 0, nums.length);
}
// 查找指定区间(左闭右开)内的最大值对应的索引
public int findMaxIndex(int[] nums, int start, int end){
int max = nums[start];
int idx = start;
for (int i = start; i < end; i++){
if (nums[i] > max){
max = nums[i];
idx = i;
}
}
return idx;
}
// 递归构建二叉树
public TreeNode getMaXTree(int[] nums, int start, int end){
if (start == end) return null;
// 获取最大值索引
int rootIdx = findMaxIndex(nums, start, end);
// 构造根节点
TreeNode root = new TreeNode(nums[rootIdx]);
// 根据最大值索引划分左子树和右子树区间(左闭右开),构造左子树和右子树
root.left = getMaXTree(nums, start, rootIdx);
root.right = getMaXTree(nums, rootIdx + 1, end);
return root;
}
}
【617.合并二叉树】简单题
关键:确定终止条件
如果其中一个二叉树的根节点为null,则不需要覆盖,直接用另一个二叉树返回
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
// 确定终止条件:
// 如果其中一个二叉树的根节点为null,则不需要覆盖,直接用另一个二叉树返回就行
// 如果都是null,第一个if中root1 = null,会自动返回root2的null
if (root1 == null) return root2;
if (root2 == null) return root1;
// 确定单层递归逻辑
TreeNode root = new TreeNode();
root.val = root1.val + root2.val; // 两个二叉树对应节点的值相加
root.left = mergeTrees(root1.left, root2.left); // 合并左子树
root.right = mergeTrees(root1.right, root2.right); // 合并右子树
return root;
}
}
【700.二叉搜索树中的搜索】简单题
方法一 按照普通二叉树的性质搜索
思路:
- 如果当前节点的val就是目标val,则返回当前节点
- 否则,先搜索左子树,如果左子树中找到值等于目标val的节点,即leftRes不为null,则返回leftRes。
- 如果左子树中找不到值等于val的节点,即leftRes=null,则返回右子树的搜索结果。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null) return null;
// 如果当前节点的val就是目标val,则返回当前节点
if (root.val == val) return root;
// 否则,先搜索左子树,如果左子树中找到值等于val的节点,即leftRes不为null,则返回leftRes。
TreeNode leftRes = searchBST(root.left, val);
if (leftRes != null) return leftRes;
// 如果左子树中找不到值等于val的节点,即leftRes=null,则返回右子树的搜索结果
else return searchBST(root.right, val);
}
}
方法二 按照二叉搜索树的性质搜索
【二叉搜索树】
二叉搜索树是一个有序树:(左节点 < 根节点 < 右节点)
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
思路:
- 如果当前节点的val就是目标val,则返回当前节点
- 如果当前节点的val大于目标val,证明val只可能出现在当前节点的左子树中,返回左子树的搜索结果。
- 如果当前节点的val小于目标val,证明val只可能出现在当前节点的右子树中,返回右子树的搜索结果。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
// 确定终止条件
if (root == null) return null;
// 如果当前节点的val就是目标val,则返回当前节点
if (root.val == val) return root;
// 如果当前节点的val大于目标val,证明val只可能出现在当前节点的左子树中,返回左子树的搜索结果
else if (root.val > val) return searchBST(root.left, val);
// 如果当前节点的val小于目标val,证明val只可能出现在当前节点的右子树中,返回右子树的搜索结果
else return searchBST(root.right, val);
}
}
【98.验证二叉搜索树 】中等题
方法一 有效的二叉搜索树的中序遍历数组为升序数组(容易理解)
思路:
- 递归,中序遍历,存放所有节点的val
- 判断中序遍历数组是否是升序数组
注意:数组前后元素不能等于,要严格小于才是升序
class Solution {
// 定义存放所有节点的val的数组
List<Integer> nodes = new ArrayList<>();
// 递归,中序遍历,存放所有节点的val
public void inorder(TreeNode root){
if (root == null) return;
inorder(root.left);
nodes.add(root.val);
inorder(root.right);
}
public boolean isValidBST(TreeNode root) {
// 递归,中序遍历,存放所有节点的val
inorder(root);
// 判断数组是否保持升序,至少有一个节点,如果只有一个节点,则不进入for循环
for (int i = 0; i < nodes.size() - 1; i++){
if (nodes.get(i) >= nodes.get(i+1)) return false; // 注意:不能等于,要严格小于才是升序
}
return true;
}
}
方法二 中序遍历二叉树过程中检测是否升序(难度较大)
粗暴版
思路:
1、先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。
2、如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre值,并返回右子树是否为有效二叉树的判断结果,作为该树是否为有效二叉搜索树的结果。
3、如果上面步骤1的两个条件不符合,则不需要再遍历右子树,直接返回false即可。
class Solution {
long pre = - (long) Math.pow(2, 31) - 1; // 节点值的最小值为-2147483648,即-2^31,pre的初始值要比这个最小值小
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
// 先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。
if(isValidBST(root.left) && root.val > pre){
// 如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre值,并返回右子树是否为有效二叉树的判断结果
pre = root.val;
return isValidBST(root.right);
}
// 如果上面两个条件不符合,则不需要再遍历右子树,直接返回false即可
return false;
}
}
优点:如果左子树已经不是有效的二叉搜索树,或者当前节点不是大于左子树的所有节点值,就不需要再遍历右子树,直接返回false。
优化版
思路:
如果节点的最小值 = long型的最小值,就不能像上面代码那样设置初始值。
矛盾在于pre需要初始化,才能与root.val比较,因此,可以使用TreeNode对象的val记录pre值,初始化为null,在比较前判断该TreeNode对象是否为null即可。
class Solution {
TreeNode pre = null; // 节点值的最小值为-2147483648,即-2^31,pre的初始值要比这个最小值小
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
// 先判断左子树是否为有效的二叉搜索树,是则判断当前节点是否大于左子树的所有节点的值。
if(isValidBST(root.left)){
// 如果当前节点是否大于左子树的所有节点的值,更新需要对比的pre.val,并返回右子树是否为有效二叉树的判断结果
if (pre == null || (pre != null && root.val > pre.val)){
pre = root;
return isValidBST(root.right);
}
}
// 如果上面两个条件不符合,则不需要再遍历右子树,直接返回false即可
return false;
}
}
【总结:所有的坑】
坑1:不能单纯的比较左节点小于中间节点,右节点大于中间节点,因为题目要求“左子树所有节点小于中间节点,右子树所有节点大于中间节点”。
坑2:最小值是-2^31,比较的初始值pre要设置比这个最小值小,但是int型变量的最小值也是-2^31,因此要设置为long型
方法三 统一迭代 - 中序遍历
思路:按照中序遍历,在处理节点时,判断节点的值是否比上一个访问的节点的值大,是则中序遍历数组为升序数组,否则返回false。
注意:判断条件的pre.val >= cur.val不要漏了等号,要严格小于才对。
class Solution {
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
TreeNode cur = root; // 至少有一个节点,root不可能为null
TreeNode pre = null;
stack.push(cur);
while (!stack.isEmpty()){
cur = stack.pop();
if (cur != null){
// 按中序遍历的反序【右 - 中 - null - 左】入栈
if (cur.right != null) stack.push(cur.right);
stack.push(cur);
stack.push(null);
if (cur.left != null) stack.push(cur.left);
}
else{
cur = stack.pop();
// 如果前面的节点【大于或者等于】当前节点的值,证明不是升序数组,直接返回false
if (pre != null && pre.val >= cur.val) return false;
pre = cur;
}
}
return true;
}
}