代码随想录第二十一天|● 530 ● 501 ● 236

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

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

差值是一个正数,其数值等于两值之差的绝对值。

思路

与98. 类似,可以利用中序遍历,将二叉搜索树转换为一个 递增的数组,然后遍历数组即可。

但其实,在中序遍历的过程中,就可以求解。

递归:利用pre记录前一个节点,然后用root(当前节点) 的值减去pre的值,更新result

迭代:中序遍历的迭代写法;同样需要用pre记录前一个节点。

递归

class Solution {
    TreeNode pre = null;
    int result = Integer.MAX_VALUE;
    public int getMinimumDifference(TreeNode root) {
        if(root == null) return 0;
        getMin(root);
        return result;
    }
    public void getMin(TreeNode root){
        if(root == null) return;
        getMin(root.left);

        if(pre != null){
            result = Math.min(result, root.val - pre.val);
        }
        pre = root;

        getMin(root.right);
    }
}

迭代

class Solution {
    TreeNode pre = null;
    public int getMinimumDifference(TreeNode root) {
        if(root == null) return 0;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode cur = root;
        int result = Integer.MAX_VALUE;
        while(!stack.isEmpty() || cur != null){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{ //此时无左节点,栈顶为最左下节点,cur为null
                cur = stack.pop();
                if(pre != null){
                    result = Math.min(result, cur.val - pre.val);
                }
                pre = cur;
                cur = cur.right;
            }
        }
        return result;
    }
}

总结

解题思维是一样的,区别在于遍历的方式而已。

都需要利用pre记录前一个节点,注意下添加pre的写法。

● 501.二叉搜索树中的众数

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

假定 BST 满足如下定义:

  • 结点左子树中所含节点的值 小于等于 当前节点的值
  • 结点右子树中所含节点的值 大于等于 当前节点的值
  • 左子树和右子树都是二叉搜索树

思路

1、最直观的思路,遍历二叉树,利用map记录元素 与 出现频率,然后返回频率最高的元素集合

此时可以选择任意遍历方式。具体代码思路如下

        1、遍历二叉树,map记录频率

        2、对频率(map的value)排序

        3、取最高频元素

2、上述思路没有利用搜索树的性质,因为是二叉搜索树,所以中序遍历元素是有序的,在统计元素频率的时候,从头遍历,那么一定是相邻两个元素作比较,然后就把出现频率最高的元素输出就可以了。

在使用中序遍历方法的时候,注意pre的使用:

        pre指针指向前一个节点,这样每次cur(当前节点)才能和pre(前一个节点)作比较。

而且初始化的时候pre = NULL,这样当pre为NULL时候,我们就知道这是比较的第一个元素。

代码如下:

        //计数
        if(pre == null || pre.val != root.val){
            count = 1;
        }else{
            count++;
        }
        pre = root;

然后是如何得到频率最高的元素:

        因为要求最大频率的元素集合(注意是集合,不是一个元素,可以有多个众数),如果是数组上大家一般怎么办?应该是先遍历一遍数组,找出最大频率(maxCount),然后再重新遍历一遍数组把出现频率为maxCount的元素放进集合。(因为众数有多个)

        这种方式遍历了两遍数组。那么我们遍历两遍二叉搜索树,把众数集合算出来也是可以的。但这里其实只需要遍历一次就可以找到所有的众数。

        那么如何只遍历一遍呢?

        如果 频率count 等于 maxCount(最大频率),当然要把这个元素加入到结果集中(以下代码为result数组),代码如下:

        if(count == maxCount){
            resList.add(root.val);
        }

        这个maxCount此时还不是真正最大频率,所以下面要做如下操作:

        频率count 大于 maxCount的时候,不仅要更新maxCount,而且要清空结果集(以下代码为result数组),因为结果集之前的元素都失效了。

         if(count > maxCount){
            resList.clear();
            maxCount = count;
            resList.add(root.val);
        }

由此,所有关键逻辑完成,代码如下

递归 

class Solution {
    TreeNode pre;
    int count;
    int maxCount;
    ArrayList<Integer> resList;

    public int[] findMode(TreeNode root) {
        resList = new ArrayList<>();
        count = 0;
        maxCount = 0;
        pre = null;

        find(root);
        int[] res = new int[resList.size()];
        for (int i = 0; i < resList.size(); i++) {
            res[i] = resList.get(i);
        }
        return res;
    }
    public void find(TreeNode root){
        if(root == null) return;
        find(root.left);

        //计数
        if(pre == null || pre.val != root.val){
            count = 1;
        }else{
            count++;
        }
        pre = root;

        //更新结果,注意此处处理使得不需要遍历第二次
        if(count > maxCount){
            resList.clear();
            maxCount = count;
            resList.add(root.val);
        }else if(count == maxCount){
            resList.add(root.val);
        }


        find(root.right);
    }
}

迭代

迭代法与递归法思路完全相同,只是遍历的方式不一样

class Solution {
    public int[] findMode(TreeNode root) {
        TreeNode pre = null;
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> result = new ArrayList<>();
        int maxCount = 0;
        int count = 0;
        TreeNode cur = root;
        while (cur != null || !stack.isEmpty()) {
            if (cur != null) {
                stack.push(cur);
                cur =cur.left;
            }else {
                cur = stack.pop();
                // 计数
                if (pre == null || cur.val != pre.val) {
                    count = 1;
                }else {
                    count++;
                }
                // 更新结果
                if (count > maxCount) {
                    maxCount = count;
                    result.clear();
                    result.add(cur.val);
                }else if (count == maxCount) {
                    result.add(cur.val);
                }
                pre = cur;
                cur = cur.right;
            }
        }
        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);
	}
}

● 236. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

思路

自底向上-回溯-后序遍历

代码随想录 (programmercarl.com)

递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

        如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
        如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (本题情况)
        如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(lc112的情况)

如何处理返回值:

如果递归函数有返回值,如何区分要搜索一条边,还是搜索整个树呢?

        搜索一条边的写法:

if (递归函数(root->left)) return ;

if (递归函数(root->right)) return ;

        搜索整棵树的写法: 

left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理;         // 中 

结论:在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)。 

递归

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return find(root, p, q);
    }
    public TreeNode find(TreeNode root, TreeNode p, TreeNode q){
        if(root == q || root == p || root == null) return root;
        TreeNode left = find(root.left, p, q);
        TreeNode right = find(root.right, p, q);
        if(left == null && right == null) return null;
        else if(left != null && right == null) return left;
        else if(left == null && right != null) return right;
        else return root;
    }
}

  • 13
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值