java算法day18
- 617 合并二叉树
- 700 二叉搜索树中的搜索
- 98 验证二叉搜索树
617 合并二叉树
正如题目所说,那么看起来这个题就是两个树同时做递归左右子树,然后在这个过程中做合并,构建树。并且从上到下处理,那么就是先序遍历。
构建树可以有两种做法。
法1:每层新new一个节点,在归的过程中建立联系。
昨天在后序和中序遍历构建二叉树中学到了一种构建树的新方法,每层都new一个新节点,然后对其左右子树进行递归,在返回的时候建立节点与节点之间的联系,所以返回值就是该层构建好的节点,由于是归的过程建立的联系,那么归到某一层的时候,该层返回的节点,他的下面的结构都是已经构造好了。
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
// 如果两个节点都为空,返回null
if (root1 == null && root2 == null) {
return null;
}
// 计算新节点的值
int value = 0;
if (root1 != null) {
value += root1.val;
}
if (root2 != null) {
value += root2.val;
}
// 创建新节点
TreeNode newNode = new TreeNode(value);
// 递归处理左子树
if (root1 == null) {//必须传null,传root1.left就会空指针。点不出来的,下面同
newNode.left = mergeTrees(null, root2.left);
} else if (root2 == null) {
newNode.left = mergeTrees(root1.left, null);
} else {
newNode.left = mergeTrees(root1.left, root2.left);
}
// 递归处理右子树
if (root1 == null) {
newNode.right = mergeTrees(null, root2.right);
} else if (root2 == null) {
newNode.right = mergeTrees(root1.right, null);
} else {
newNode.right = mergeTrees(root1.right, root2.right);
}
return newNode;
}
}
该做法的缺点很明显,1.要写很多的if判断来走哪种递归方式,不分情况传就会空指针异常。
2.费空间,每一层都会new一个新的节点。
法2:原地构建
本质思路还是同时递归左右子树,只是处理过程有差别。本题是在root1上进行构建
这种方法就抓住一句话。
修改指针可以是很方便的方式
这个代码建议先看中间,然后再去想递归出口
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
//由于是同时递归下去的,二者到下层时要先判断,有没有一方为空,想想如果有一方为空,另一方不为空,那么空的一方后面就已经没有节点了,此时直接返回非空的部分,不就是代表后面的内容全是这个非空的部分。而这里就是改个指针的功夫。后面也没必要走了。
//看代码,这里如果root1是null那么后面就拼root2的部分。
//如果root1不为null,那么检查root2,如果root2为null,那么后面的结果都是root1的,直接返回,后面不用再进行构建了。
//而且如果都为null,这里就会返回null。只能说很妙。
if (root1 == null) return root2;
if (root2 == null) return root1;
//两个节点进行合并
root1.val += root2.val;
//递归构建root1的左右子树
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
//最后完成构建是在root1上,所以返回root1。这就是结果
return root1;
}
}
700 二叉搜索树中的搜索
依据二叉搜索树的性质,不断的调整往下的递归方向就可以做出来。
因为BST的性质,如果找不到就会一直往下搜,直到碰到null。找得到就停了。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if(root==null){
return null;
}
if(val<root.val){
return searchBST(root.left,val);
}else if(val>root.val){
return searchBST(root.right,val);
}else{
return root;
}
}
}
98 验证二叉搜索树
验证二叉搜索树的核心思路:
要知道二叉搜索树的判定:节点从上到下递归满足左子树小于根节点,根节点小于右子树。
我一开始顺着这个思路做就想到了双重递归,从上到下的去验证每个节点是否满足这个定义,这个做法是非常不好的。一看就是有大量的冗余运算。
所以本题用的方法,我称之为范围限制和束缚传递。
具体做法:
1.为树中的每个节点定义一个有效值的范围,这个范围有一个下界和一个上界定义。
2.初始时根节点可以是任何值,其范围是正无穷和负无穷,那么此时就可以用MAX常量。
3.递归定义子节点的范围,对于任何节点,其左子节点的值必须小于该节点的值,单要大于该节点的下界。其右子节点的值必须大于该节点的值,并且小于该节点的上界。
4.递归的时候就要不断的更新范围,节点必须要在该范围内才能满足二叉搜索树的性质。
5.范围更新规则:递归左子树,上界变为当前节点的值,下届保持不变。
递归右子树,下届变为当前节点的值,上界保持不变。
懂了这些就可以总结快速做法:
如果往左递归,可以把开区间的右边界更新为当前节点,如果往右递归,就把开区间的左边界更新为当前节点。
从这个图里可以看到,上层的约束,已经带到下层去了,而且这个范围在不断的收缩。
这个做法是先序遍历的做法,即从上到下
class Solution {
public boolean isValidBST(TreeNode root) {
//第一层就是root,然后范围为无穷大和无穷小,由于测试用例要求到Long了,那么就用Long的
return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
}
private boolean isValidBST(TreeNode node, long left, long right) {
//递归出口,到这里说明路上的元素都满足了
if (node == null) {
return true;
}
//把当前节点值拿出来做验证
long x = node.val;
//先序遍历的思想
//不仅本层要满足,左右子树也要满足。所以就这样递归下去
return left < x && x < right &&
isValidBST(node.left, left, x) &&
isValidBST(node.right, x, right);
}
}