力扣刷题| 530.二叉搜索树的最小绝对差、501.二叉搜索树中的众数、236. 二叉树的最近公共祖先

LeetCode 530.二叉搜索树的最小绝对差

题目链接🔗

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.二叉搜索树中的众数

题目链接🔗

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. 二叉树的最近公共祖先

题目链接🔗

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;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值