530.二叉搜索树的最小绝对差
题目:力扣
刚开始想的是最小绝对差肯定在叶子节点处出现 - 但是其实也不绝对,最小绝对值可能会发生在路径中。
这里要利用二叉树的特性 - 二叉树是有序的,中序遍历可以得到这个有序数组。
因此其实最简单的一个想法就是 - 用一个数组储存二叉树中遍历的元素,然后遍历这个数组,判断相邻的元素最小绝对值差。
class Solution {
public int getMinimumDifference(TreeNode root) {
List<Integer> list = new ArrayList<>();
Helper(root, list);
//找最小差值
int result = Integer.MAX_VALUE;
for(int i=1; i< list.size(); i++){
int dis = Math.abs(list.get(i) - list.get(i-1));
if( dis < result){
result = dis;
}
}
return result;
}
//中序遍历
public void Helper(TreeNode root, List<Integer> list){
if(root == null){
return;
}
Helper(root.left, list);
list.add(root.val);
Helper(root.right, list);
}
}
当然这个差值比较过程可以在二叉树遍历的时候就进行,一步到位。
这里进行递归的时候就需要储存前一个节点的值。
在进行实现的时候有个小细节要注意:这里的不能用pre进行传参,如果这个辅助方法是:
Helper(TreeNode root,TreeNode pre)
那么刚开始的时候pre都是null,经过第一个左节点后,pre更新成为了左节点,但是返回之后,pre又变成了原来的null。
因此传参会存在问题。
这里可以把pre当作一个全局变量放在外部。
每次对pre进行更新就可/
class Solution {
public int getMinimumDifference(TreeNode root) {
Helper(root);
return dis;
}
//中序遍历
int dis = Integer.MAX_VALUE;
TreeNode pre = null;//前一个节点
public void Helper(TreeNode root){
if(root == null){
return;
}
Helper(root.left);//左
// 中间节点
if(pre != null){
dis = Math.min(Math.abs(pre.val - root.val), dis);
}
pre = root;
Helper(root.right);//右
}
}
参考资料:代码随想录
501.二叉搜索树中的众数
题目:力扣
看到题的第一种想法:
把整个树遍历一遍,用map来统计数字出现的频率,然后取出频率最高的;
但是这个地方其实是二叉树,可以利用二叉树的特性
二叉树中序遍历的元素是有序的。
因此相同大小的元素是相邻出现的;可以遍历时直接更新最大频次。
按照最小绝对值的做法,可以将树用中序遍历一遍,取出数组;然后再对数组遍历一遍,找到最大频次的元素。
此时又有问题了,因为要求最大频率的元素集合(注意是集合,不是一个元素,可以有多个众数),如果是数组上大家一般怎么办?
应该是先遍历一遍数组,找出最大频率(maxCount),然后再重新遍历一遍数组把出现频率为maxCount的元素放进集合。(因为众数有多个)
这种方式遍历了两遍数组。
那么我们遍历两遍二叉搜索树,把众数集合算出来也是可以的。
但这里其实只需要遍历一次就可以找到所有的众数。
那么如何只遍历一遍呢?
如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中;
频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了。
代码实现:
class Solution {
public int[] findMode(TreeNode root) {
List<Integer> list = new ArrayList<>();
Helper(root, list);
List<Integer> result = new ArrayList<>();
int max_count = 0;
int count = 0;
int value = -1;
//只遍历一遍,如果和当前的最大频率相同则加入结果集;如果有发现更大的频率,则清空结果集,重新加入
for(int i=0; i<list.size(); i++){
//先计算count
if(list.get(i) == value){
count++;
}else{
count = 1;
value = list.get(i);
}
//再更新结果集
if(count == max_count){
result.add(list.get(i));
}
if(count > max_count){
max_count = count;
result.clear();
result.add(list.get(i));
}
}
int[] re = new int[result.size()];
for(int i=0; i<result.size(); i++){
re[i] = result.get(i);
}
return re;
}
public void Helper(TreeNode root, List<Integer> list){
if(root == null){
return;
}
Helper(root.left, list);
list.add(root.val);
Helper(root.right, list);
}
}
直接采用中序遍历并且处理最大频次结果集:
class Solution {
public int[] findMode(TreeNode root) {
Helper(root);
int[] re = new int[result.size()];
for(int i=0; i<result.size(); i++){
re[i] = result.get(i);
}
return re;
}
TreeNode pre;
int max_count = 0;
int count = 0;
List<Integer> result = new ArrayList<>();
public void Helper(TreeNode root){
if(root == null){
return;
}
Helper(root.left);
//先计算count
if(pre == null){
count=1;
}else if(root.val == pre.val){
count++;
}else{
count = 1;
}
//再更新结果集
if(count == max_count){
result.add(root.val);
}
if(count > max_count){
max_count = count;
result.clear();
result.add(root.val);
}
//更新上一个节点
pre = root;
Helper(root.right);
}
}
参考资料:代码随想录
236. 二叉树的最近公共祖先
题目:力扣
要找最近公共祖先,第一反应肯定是要自底向上寻找这个节点。
但是二叉树的构造并没有自底向上这个方向。
然而,二叉树的后序遍历就是自底向上向上的遍历过程。
确定二叉树的遍历顺序之后,需要判断二叉树的最近公共祖先情况。
这道题主要是需要对题干中的条件理解清楚,有些比较复杂的情况,但是因为题干中的限制,其实仍然可以按照一个简单的后序遍历逻辑解决。
情况一:
节点1在左子树,节点2在右子树。
情况二:
递归逻辑:
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。
递归步骤:
1. 输入和输出:输入(根节点,节点q,节点p);输出(公共祖先节点)
2. 终止条件:若遇到节点q,则返回q;若遇到节点p,则返回p;若节点位空则返回null。
3. 单层递归逻辑:先向左遍历,获得左子树的返回节点;再向右遍历,获得右子树的返回节点。然后判断左右子树返回节点的情况
1)如果左右子树返回的节点都不为空,说明此处的根节点就是最近的公共祖先(注意题中的条件 - 树中所有的元素都不相同,p不等于q,因此p和q在二叉树中是唯一的,不存在找到的两个节点都是p或者都是q的情况;而在遍历的时候采取后序遍历的方式,因此肯定是自底向上,最小的);
2)如果左子树返回的节点都为空而右子树不为空,说明此处的右子树节点就是最近的公共祖先;
3)如果右子树返回的节点都为空而左子树不为空,说明此处的左子树节点就是最近的公共祖先;
代码实现:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//终止条件
if(root == null || root == q || root == p){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);//左子树
TreeNode right = lowestCommonAncestor(root.right, p, q);//右子树
TreeNode result = null;
if(left != null && right != null){
result = root;
}else if(left == null){
result = right;
}else{
result = left;
}
//返回值
return result;
}
总结:
对于二叉树来说做题时可以考虑的点:
如果是创建一颗新的二叉树 - 考虑前序遍历;
如果需要利用二叉树的有序性 - 考虑中序遍历;
如果是需要采用自底向上获得答案 - 考虑后序遍历。