合并二叉树
题目:617
- 标准递归三部曲,毕竟就是挨个合并,主要看咋个实现
- 返回值以及函数参数:返回的是合并后的根节点,然后因为是合并,所以传入的是两个树
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;//返回合并后的根节点
二叉搜索树中的搜索
题目:700
- 首先常规递归,先分析一下此前自己写的代码为什么不对
if (root.val == val) {
return root;
}
if (root.left != null) {
return searchBST(root.left, val);
}
if (root.right != null) {
return searchBST(root.right, val);
}
return null;
- 注意看,这是先递归左子树,但是如果左子树没有要找的值,它就直接返回结果null了,甚至都不给搜索右子树的机会。是的,所以这就是错误,如果按常规来的话,应该这样
if (root == null || root.val == val) {
return root;
}
TreeNode left = searchBST(root.left, val);
if (left != null) {
return left;
}
return searchBST(root.right, val);
- 注意看,这里是先把结果放到left里,然后判断left是否为空,如果为空,说明左子树里没有要找的值,就去递归搜索右子树。如果left不为空,说明在左子树里有对应的值,找就对了。
- 常规的说完了,来看有特色的,再注意看,这是一个二叉搜索树,那么就奠定了它的排序顺序,左 < 中 < 右。故
if (val < root.val) {
return searchBST(root.left, val);
} else {
return searchBST(root.right, val);
}
验证二叉搜索树
题目:98
- 哎呀哎呀,这个我会,如果是二叉搜索树的话,那么把它中序遍历一下,会发现是递增的,所以可以先把它中序遍历,遍历结果放在动态数组里,然后遍历动态数组进行验证
- 不要忘了调用中序遍历的方法x
- 递归也可以参考中序遍历思路,不断递归然后更新maxVal,一旦maxVal >= root.val(还没更新的值) 就是错的。
- 如果递归时遇到空节点,说明到头了,空节点也是搜索二叉树(确信
class Solution {
// 递归
TreeNode max;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
// 左 检验左子树是否为搜索二叉树
boolean left = isValidBST(root.left);
if (!left) {
return false;
}
// 中
if (max != null && root.val <= max.val) { //把检验是否是搜索二叉树落实到行动上
return false;
}
max = root;//更新max
// 右
boolean right = isValidBST(root.right); //再去看看右子树是否为搜素二叉树
return right;
}
}
二叉搜索树的最小绝对差
题目:530
- 因为是二叉搜索树,所以按照中序遍历,就能得到一个递增的列表,然后遍历列表比较差值最小值即可
- 还有一个递归,是每次记录上一次的结点,即在进行中序遍历的递归时,进行差值的比较和对节点的记录
- 这里的pre是全局的
class Solution {
TreeNode pre;// 记录上一个遍历的结点
int result = Integer.MAX_VALUE;
traversal(root.left);
//中
if(pre!=null){
result = Math.min(result,root.val-pre.val);
}
pre = root;
//右
traversal(root.right);
二叉搜索树的众数
题目:501
- 初步思路是二叉树普遍可用的方法,即一边遍历,一边把节点值和其出现次数计入HashMap,然后对HashMap进行排序,遍历输出
- 看不太懂对吧......是的,暴力破解法思路很暴力,但是这个代码搞不来
List<Map.Entry<Integer, Integer>> mapList = map.entrySet().stream()
.sorted((c1, c2) -> c2.getValue().compareTo(c1.getValue()))
.collect(Collectors.toList());
list.add(mapList.get(0).getKey());
// 把频率最高的加入 list
for (int i = 1; i < mapList.size(); i++) {
if (mapList.get(i).getValue() == mapList.get(i - 1).getValue()) {
list.add(mapList.get(i).getKey());
} else {
break;
}
}
return list.stream().mapToInt(Integer::intValue).toArray();
- 还是看一下更符合该题意的简单方式吧,利用二叉搜索树的性质。
- 因为是二叉搜索树,所以中序遍历的话,它是排好序的,去找一个数出现的频率也好找,因为都挨在一起
- 需要用到的有
class Solution {
ArrayList<Integer> resList;//记录出现次数为maxCount的节点值
int maxCount;//不断更新,以找到最多的频率
int count;//累计节点值出现次数,然后用来与maxCount比较
TreeNode pre;//记录上一次递归的节点,来比较上一个节点跟现在的节点是否相同
- 对列表的更新操作
if (count > maxCount) {//找到了新的出现频率高的值
resList.clear();//清空列表
resList.add(rootValue);//更新列表
maxCount = count;//更新最大值
} else if (count == maxCount) {
resList.add(rootValue);//其他的众数(出现频率一致
}
- 还要不断更新pre节点
- 其他的就是递归套路,中序遍历的递归套路,和上一题差不多的样子
最近公共祖先
普通二叉树:236
- 初步思路,就是利用哈希表,把节点继承关系记录下来,然后不断往上找,利用哈希集合找重复元素,可行
- 递归思路
- 求最小公共祖节点,从下往上,是一个回溯,而二叉树的后序遍历是一个天然回溯
- 函数返回值和参数:需要返回值,以告诉我们是否找到了节点,而且不仅要找到特定节点,最后结果还是要找它们的公共节点,故返回值为节点。原函数即可
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
- 结束条件:找到特定节点(p q)或者到了头,就返回
if (root == null || root == p || root == q) { // 递归结束条件
return root;
}
- 单层递归逻辑:用两个返回量 left right来确定当前节点的左右子树是否有特定节点。如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解
如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null) { // 若未找到节点 p 或 q
return null;
}else if(left == null && right != null) { // 若找到一个节点
return right;
}else if(left != null && right == null) { // 若找到一个节点
return left;
}else { // 若找到两个节点
return root;
}
搜索二叉树:235
- 感觉和上一个可以用同一套代码,但是它是搜索二叉树,应该有更好的
- 根据搜索二叉树的特性:在遍历二叉搜索树的时候就是寻找区间[p.val, q.val] 那么如果 cur.val 大于 p.val,同时 cur.val 大于q.val,那么就应该向左遍历(说明目标区间在左子树上)。如果cur.val在这个区间内,说明p q两节点在它的左右子树,那么cur确实就是p q的最近公共祖节点
需要注意的是此时不知道p和q谁大,所以两个都要判断
二叉搜索树中的插入操作
题目:701
- 递归返回值和参数:可以利用返回值完成新加入的节点与其父节点的赋值操作。
public TreeNode insertIntoBST(TreeNode root, int val)
- 终止条件:终止条件就是找到遍历的节点为null的时候,就是要插入节点的位置了,并把插入的节点返回。
if (root == null) // 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
return new TreeNode(val);
- 单层递归:利用二叉搜索树的性质,决定是去搜索左子树还是右子树。感觉单层递归可以就以第一层递归为例,毕竟每一层都这样,第一层还好想
if (root.val < val){
root.right = insertIntoBST(root.right, val); // 递归创建右子树
}else if (root.val > val){
root.left = insertIntoBST(root.left, val); // 递归创建左子树
}
return root;//递归后的结果,第一层里的结果就是要返回根节点