530.二叉搜索树的最小绝对差
视频讲解:https://www.bilibili.com/video/BV1DD4y11779
本题思路和98.验证二叉搜索树类似,利用二叉搜索树的中序遍历递增的特性
class Solution {
// 记录前一个节点
TreeNode per = null;
int Min = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if(root == null) return 0;
getMinimumDifference(root.left); // 左
if(per!=null && (root.val-per.val)<Min){ //中
Min = root.val-per.val;
}
// 第一次per为空,per就会记录叶子节点
// 然后回退到父节点,那么per就是当前节点的前一个节点
per = root;
getMinimumDifference(root.right); // 右
return Min;
}
}
501.二叉搜索数中的众数
视频讲解:https://www.bilibili.com/video/BV1fD4y117gp
// 解法一
class Solution {
public int[] findMode(TreeNode root) {
List<Integer> res = new ArrayList<>();
Map<Integer,Integer> map = new HashMap<>();
if(root == null) return new int[]{};
// 二叉树转换成有序集合
find(root,res);
// 将有序集合存储到map中[值,出现的次数]
for(int num:res){
map.put(num,map.getOrDefault(num,0)+1);
}
int maxNum = Integer.MIN_VALUE;
// 遍历Map集合,找出众数
for(Map.Entry<Integer,Integer> enter : map.entrySet()){
// 当前次数 == 最大次数:直接存入结res集合
if(enter.getValue() == maxNum){
res.add(enter.getKey());
}
// 当前次数 > 最大次数
// 那么就更新maxNum,并且清空res集合
// 因为有新的值大于最大次数了,所以不仅要更新maxNum,还要清空掉之前存储的结果
// 再将新的符合要求的值存入
if(enter.getValue() > maxNum){
maxNum = enter.getValue();
res.clear();
res.add(enter.getKey());
}
}
// 将集合转换成数组
int[] arr = new int[res.size()];
for(int i=0; i<res.size(); i++){
arr[i] = res.get(i);
}
return arr;
}
// 利用中序遍历把二叉树存入集合中
public void find(TreeNode cur,List<Integer> res){
if(cur == null) return ;
find(cur.left,res);
res.add(cur.val);
find(cur.right,res);
}
}
// 解法二
class Solution {
TreeNode per = null; // 记录前一个节点
int maxCount = 1; // 最大次数
int count = 0; // 当前值出现次数
List<Integer> res = new ArrayList<>(); // 结果集
public int[] findMode(TreeNode root) {
find(root);
int[] arr = new int[res.size()];
for(int i=0; i<res.size(); i++){
arr[i] = res.get(i);
}
return arr;
}
// 利用中序遍历把二叉树存入集合中
public void find(TreeNode cur){
if(cur == null) return ;
find(cur.left); // 左
// 遍历到叶子节点,就把当前值出现的次数初始化为1
if(per == null) count=1;
// 如果前一节点 == 当前节点,count就加一
else if(per.val == cur.val) count++;
// 否则表示cur是新的数值,就把count更新为1
else count = 1;
per=cur;
// 收集结果
// 当前值次数 == 最大次数,就把当前值存入结果集
if(count == maxCount) res.add(cur.val);
// 当前值次数 > 最大次数,那么更新maxCount,并且清空res集合,再将新的值存入
// 因为最大次数,并不是遍历完一次二叉树后找出来的,是在遍历的过程中不断更新的
// 所以当有新值的出现次数>最大次数时
// 不止要更新最大次数,还要清空掉存储的结果,因为都不符合要求了,再将新的符合要求的值存入
if(count > maxCount){
maxCount = count;
res.clear();
res.add(cur.val);
}
find(cur.right); // 右
}
}
236.二叉树的最近公共祖先
视频讲解:https://www.bilibili.com/video/BV1jd4y1B7E2
题目分析:
-
满足什么条件才算公共祖先?
- 情况一:p和q,当前节点的左孩子存在p,右孩子存在q,那么当前节点就是pq节点的公共祖先
- 情况二:p和q1,此时直接就可以把q1返回回去,因为当前q1就是公共祖先
- 为什么可以不去判断p直接返回q1呢?
- 因为题目要求p和q1是一定会存在,不会有只存在一个节点的情况
- 那么如果p不在q1的左右孩子里呢?
- 如果q1的左右孩子不存在p,那么我们不就是返回到了情况一的步骤里面了嘛
-
既然查找公共祖先,我们肯定就要自底向上进行查找,那应该用什么遍历顺序?
- 既然是自底向上查找,那么我们就一定是使用后序遍历,向上查找那么就记录二叉树的高度,高度就是后序遍历
-
注意:遍历二叉树一定是要把整个二叉树都遍历一遍,
-
可能如图q1已经是公共祖先,但是我们还是要查找一遍根节点的右孩子
-
因为我们是根据左右节点的返回值来处理中间节点
-
class Solution {
// 返回值:因为我们要知道左右孩子中是否存在pq节点,所以我们就要不断返回节点值
// 参数:要不断传入当前节点和pq目标节点
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// 终止条件:查找到了pq节点就开始返回
if(root == null) return null;
if(root==p || root==q) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q); // 左
TreeNode right = lowestCommonAncestor(root.right,p,q); // 右
// 中
// 如果当前节点的左右孩子都不为空,那么当前节点就是公共祖先,不断像向上返回
if(left!=null && right!=null) return root;
// 只查找到一边存在p或者q,那么就向上返回查找到的哪一个
// 直到某个的节点的另一边也查找到另外一个节点,那么这个节点就是公共祖先
if(left==null && right!=null) return right;
if(left!=null && right==null) return left;
return null;
}
}
直到某个的节点的另一边也查找到另外一个节点,那么这个节点就是公共祖先
if(leftnull && right!=null) return right;
if(left!=null && rightnull) return left;
return null;
}
}