LeetCode 530.二叉搜索树的最小绝对差
题目链接🔗
思路
注意是二叉搜索树,二叉搜索树可是有序的。
遇到在二叉搜索树上求什么最值啊,差值之类的,就把它想成在一个有序数组上求最值,求差值,这样就简单多了。
那么二叉搜索树采用中序遍历,其实就是一个有序数组。
在一个有序数组上求两个数最小差值,这是不是就是一道送分题了。
最直观的想法,就是把二叉搜索树转换成有序数组,然后遍历一遍数组,就统计出来最小差值了。
其实在二叉搜素树中序遍历的过程中,我们就可以直接计算了。
需要用一个pre节点记录一下cur节点的前一个节点。
class Solution {
TreeNode pre;// 记录上一个遍历的结点
int result = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if(root==null)return 0;
traversal(root);
return result;
}
public void traversal(TreeNode root){
if(root==null)return;
//左
traversal(root.left);
//中
if(pre!=null){
result = Math.min(result,root.val-pre.val);
}
pre = root;
//右
traversal(root.right);
}
}
LeetCode 501.二叉搜索树中的众数
题目链接🔗
思路
如果不是二叉搜索树
如果不是二叉搜索树,最直观的方法一定是把这个树都遍历了,用map统计频率,把频率排个序,最后取前面高频的元素的集合。
迭代法
class Solution {
public int[] findMode(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
Map<Integer, Integer> map = new TreeMap<>();
List<Integer> result = new ArrayList<>();
if (root == null) {
return null;
}
que.offer(root);
while (!que.isEmpty()) {
int len = que.size();
for (int i = 0; i < len; i++) {
TreeNode node = que.poll();
map.put(node.val, map.getOrDefault(node.val, 0) + 1);
if (node.left != null) {
que.offer(node.left);
}
if (node.right != null) {
que.offer(node.right);
}
}
}
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));
int i = 0;
int val = 0;
for (Map.Entry<Integer, Integer> entry : list) {
if (i == 0){
val = entry.getValue();
}
if (entry.getValue() == val){
result.add(entry.getKey());
}
i++;
}
return result.stream().mapToInt(Integer::intValue).toArray();
}
}
递归法
class Solution {
public int[] findMode(TreeNode root) {
Map<Integer, Integer> map = new HashMap<>();
List<Integer> list = new ArrayList<>();
if (root == null) return list.stream().mapToInt(Integer::intValue).toArray();
// 获得频率 Map
searchBST(root, map);
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();
}
void searchBST(TreeNode curr, Map<Integer, Integer> map) {
if (curr == null) return;
map.put(curr.val, map.getOrDefault(curr.val, 0) + 1);
searchBST(curr.left, map);
searchBST(curr.right, map);
}
}
如果是二叉搜索树
既然是搜索树,它中序遍历就是有序的。
递归法
class Solution {
ArrayList<Integer> resList;
int maxCount;
int count;
TreeNode pre;
public int[] findMode(TreeNode root) {
resList = new ArrayList<>();
maxCount = 0;
count = 0;
pre = null;
findMode1(root);
int[] res = new int[resList.size()];
for (int i = 0; i < resList.size(); i++) {
res[i] = resList.get(i);
}
return res;
}
public void findMode1(TreeNode root) {
if (root == null) {
return;
}
findMode1(root.left);
int rootValue = root.val;
// 计数
if (pre == null || rootValue != pre.val) {
count = 1;
} else {
count++;
}
// 更新结果以及maxCount
if (count > maxCount) {
resList.clear();
resList.add(rootValue);
maxCount = count;
} else if (count == maxCount) {
resList.add(rootValue);
}
pre = root;
findMode1(root.right);
}
}
LeetCode 236. 二叉树的最近公共祖先
题目链接🔗
思路
遇到这个题目首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了。
那么二叉树如何可以自底向上查找呢?
回溯啊,二叉树回溯的过程就是从低到上。
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
接下来就看如何判断一个节点是节点q和节点p的公共祖先呢。
首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。 即情况一:
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。
但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q§。 情况二:
其实情况一 和 情况二 代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。
因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本身就是 公共祖先的情况。
递归三部曲:
-
确定递归函数返回值以及参数
需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p。
TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
-
确定终止条件
那么我们来说一说,如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值,后面在中节点的处理过程中会用到,那么中节点的处理逻辑,下面讲解。if (root == q || root == p || root == null) return root;
-
确定单层递归逻辑
先用left和right接住左子树和右子树的返回值,代码如下:TreeNode left = lowestCommonAncestor(root.left, p, q); TreeNode right = lowestCommonAncestor(root.right, p, q);
如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解
如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然。
如图:
图中节点10的左子树返回null,右子树返回目标值7,那么此时节点10的处理逻辑就是把右子树的返回值(最近公共祖先7)返回上去!那么如果left和right都为空,则返回left或者right都是可以的,也就是返回空。
那么寻找最小公共祖先,完整流程图如下:
从图中,大家可以看到,我们是如何回溯遍历整棵二叉树,将结果返回给头结点的!
完整代码如下:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || 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) { // 若未找到节点 p 或 q
return null;
}else if(left == null && right != null) { // 若找到一个节点
return right;
}else if(left != null && right == null) { // 若找到一个节点
return left;
}else { // 若找到两个节点
return root;
}
}
}