Day18 力扣二叉树 : 530.二叉搜索树的最小绝对差 |501.二叉搜索树中的众数 | 236. 二叉树的最近公共祖先
530.二叉搜索树的最小绝对差
需要领悟一下二叉树遍历上双指针操作,优先掌握递归
题目链接/文章讲解:https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%BB%9D%E5%AF%B9%E5%B7%AE.html
视频讲解:https://www.bilibili.com/video/BV1DD4y11779
第一印象:
感觉思路不难,看到二叉搜索树还是要想到中序遍历,因为中序遍历之后的顺序就是数字递增的序列。直接看题解。
看完题解的思路:
思路清晰,嘎嘎自信。
实现遇到的苦难:
result 和 pre要作为全局变量,pre作为全局变量感觉是二叉搜索树中序遍历的一个套路吧。
但是我又下意识觉得这两个东西可以作为参数写在函数里,但结果确实是不行的。我感觉可能因为回溯的时候参数不对,还没有去探索为什么,先学着吧,朋友叫我去打游戏了,等我二刷的时候细看一下。
感悟:
不难!
代码:
class Solution {
int result = Integer.MAX_VALUE;
TreeNode pre = null;
public int getMinimumDifference(TreeNode root) {
minimum(root);
return result;
}
private void minimum(TreeNode root) {
if (root == null) return;
//左
minimum(root.left);
//中
if (pre != null) {
result = Math.min(result, root.val - pre.val);
}
pre = root;
//右
minimum(root.right);
}
}
501.二叉搜索树中的众数
和 530差不多双指针思路,不过 这里涉及到一个很巧妙的代码技巧。
可以先自己做做看,然后看我的视频讲解。
https://programmercarl.com/0501.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E4%BC%97%E6%95%B0.html
视频讲解:https://www.bilibili.com/video/BV1fD4y117gp
第一印象:
我想中序遍历之后,放到哈希表里,再返回。看题解!
看完题解的思路:
我的思路是对任意二叉树的,而对二叉搜索树有更好的办法,学习一哈。
比较容易想到的是先遍历一遍树,统计出最多的频率maxCount,然后再遍历一遍树,寻找出现频率和maxCount相同的。
但是这道题可以只遍历一次树就可以。在这里学到了一个代码小技巧。
中序遍历计算一个val的count的过程比较简单,但是怎么来更新这个maxCount并和maxCount比较呢?直接给出代码:
//更新maxCount
if (count == maxCount) {
result.add(root.val);
} else if (count > maxCount) {
result.clear();
result.add(root.val);
maxCount = count;
}
虽然一开始maxCount是0,好像拿maxCount和 count比较不合理啊。但是我们想象比如maxCount是2,count现在也是2,我们把val加入res,之后count变成3的时候,那我们就回进入第二个判断,清空res,再把count为3的加入,再更新maxCount。
我觉得就是 目前最多频次的val就先加入res,如果出现更多的就清空res,再把当前的加入res。这个就是小技巧
实现中的困难:
三个 if 条件我写成单独的三个了,就会出现空指针问题了。这里处理的不是终止条件,应该是if elseif的关系。
//中
if (pre == null) {
count = 1;
} else if ( pre.val == root.val) {
count++;
} else {
count = 1;
}
感悟:
确实有东西这题。
代码:
class Solution {
ArrayList<Integer> result;
TreeNode pre = null;
int maxCount = 0;
int count = 0;
public int[] findMode(TreeNode root) {
result = new ArrayList<>();
find(root);
int[] res = new int[result.size()];
for (int i = 0; i < result.size(); i++) {
res[i] = result.get(i);
}
return res;
}
private void find(TreeNode root) {
if (root == null) return;
//左
find(root.left);
//中
if (pre == null) {
count = 1;
} else if ( pre.val == root.val) {
count++;
} else {
count = 1;
}
pre = root;
//更新maxCount
if (count == maxCount) {
result.add(root.val);
} else if (count > maxCount) {
result.clear();
result.add(root.val);
maxCount = count;
}
//右
find(root.right);
}
}
236. 二叉树的最近公共祖先
本题其实是比较难的,可以先看我的视频讲解
https://programmercarl.com/0236.%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B1%E7%A5%96%E5%85%88.html
视频讲解:https://www.bilibili.com/video/BV1jd4y1B7E2
第一印象:
这道题感觉有点难,直接看题解了。
看完题解的思路:
确实难,看完也觉得晕乎的。
这道题的思路是,从下往上去看这棵树,如果一个节点的左子树有p,右子树有q(pq可以互换),那么这个节点就是最近公共祖先。那么怎么从下向上遍历这个树呢?不能的,但是我们可以从下向上处理这棵树。
怎么从下向上查找一个二叉树呢?
回溯二叉树回溯的过程就是从低到上。
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
这句话是代码随想录里的,我觉得很妙,但我还没完全体会到
所以这道题是后序遍历,因为判断一个节点的左递归有无pq,再判断右递归有无pq,最后才能说明这个节点是后序遍历,不然还没有右递归,中的逻辑没有办法写出来。
而且对于这道题有两种情况,一种是很容易想到的p,q是两个节点,他们有公共祖先。另一种是 p就是q的祖先,这种说实话我自己都没想到,但是这种情况会在第一种的代码里被顺便实现了,这个真的很神奇。
关于返回值:
实现中的困难:
我觉得这道题不是我能独立实现出来的,我需要看着题解去理解代码。
代码很简单,但是里面涉及的东西很多。
感悟:
涉及到的从下向上查找树要用回溯、后序遍历天然带有回溯、递归的返回值怎么写、情况2是怎么包含在代码里的。
这些我都没有完全想清楚。
代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//情况2 包含在这里了
if (root == null || root == q || root == p) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null && right == null) {
return null;
} else if (left == null && right != null) {
return right;
} else if (left != null && right == null) {
return left;
} else {
return root;
}
}
}