二叉树(四)

74 阅读5分钟

合并二叉树

题目:617

  • 标准递归三部曲,毕竟就是挨个合并,主要看咋个实现
  • 返回值以及函数参数:返回的是合并后的根节点,然后因为是合并,所以传入的是两个树
public TreeNode mergeTrees(TreeNode root1, TreeNode root2)
  • 终止条件:其中一方到了头,就返回另外一方接上就好
        if (root1 == null) return root2;
        if (root2 == null) return root1;
  • 单层递归:把一棵树合并到另一棵树上,把值合并,然后递归合并它的左右子树
        root1.val += root2.val;
        root1.left = mergeTrees(root1.left,root2.left);
        root1.right = mergeTrees(root1.right,root2.right);
        return root1;//返回合并后的根节点

二叉搜索树中的搜索

题目:700

  • 首先常规递归,先分析一下此前自己写的代码为什么不对
    if (root.val == val) {
        return root;
    } 
    if (root.left != null) { 
        return searchBST(root.left, val);
    }
    if (root.right != null) { 
        return searchBST(root.right, val);
    } 
    return null;
  • 注意看,这是先递归左子树,但是如果左子树没有要找的值,它就直接返回结果null了,甚至都不给搜索右子树的机会。是的,所以这就是错误,如果按常规来的话,应该这样
        if (root == null || root.val == val) {
            return root;
        }
        TreeNode left = searchBST(root.left, val);
        if (left != null) {
            return left;
        }
        return searchBST(root.right, val);
  • 注意看,这里是先把结果放到left里,然后判断left是否为空,如果为空,说明左子树里没有要找的值,就去递归搜索右子树。如果left不为空,说明在左子树里有对应的值,找就对了。
  • 常规的说完了,来看有特色的,再注意看,这是一个二叉搜索树,那么就奠定了它的排序顺序,左 < 中 < 右。故
        if (val < root.val) {
            return searchBST(root.left, val);
        } else {
            return searchBST(root.right, val);
        }

验证二叉搜索树

题目:98

  • 哎呀哎呀,这个我会,如果是二叉搜索树的话,那么把它中序遍历一下,会发现是递增的,所以可以先把它中序遍历,遍历结果放在动态数组里,然后遍历动态数组进行验证
  • 不要忘了调用中序遍历的方法x
  • 递归也可以参考中序遍历思路,不断递归然后更新maxVal,一旦maxVal >= root.val(还没更新的值) 就是错的。
  • 如果递归时遇到空节点,说明到头了,空节点也是搜索二叉树(确信
class Solution {
    // 递归
    TreeNode max;
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 左 检验左子树是否为搜索二叉树
        boolean left = isValidBST(root.left);
        if (!left) {
            return false;
        }
        // 中
        if (max != null && root.val <= max.val) { //把检验是否是搜索二叉树落实到行动上
            return false;
        }
        max = root;//更新max
        // 右
        boolean right = isValidBST(root.right); //再去看看右子树是否为搜素二叉树
        return right;
    }
}

二叉搜索树的最小绝对差

题目:530

  • 因为是二叉搜索树,所以按照中序遍历,就能得到一个递增的列表,然后遍历列表比较差值最小值即可
  • 还有一个递归,是每次记录上一次的结点,即在进行中序遍历的递归时,进行差值的比较和对节点的记录
  • 这里的pre是全局的
class Solution {
    TreeNode pre;// 记录上一个遍历的结点
    int result = Integer.MAX_VALUE;
        traversal(root.left);
        //中
        if(pre!=null){
            result = Math.min(result,root.val-pre.val);
        }
        pre = root;
        //右
        traversal(root.right);

二叉搜索树的众数

题目:501

  • 初步思路是二叉树普遍可用的方法,即一边遍历,一边把节点值和其出现次数计入HashMap,然后对HashMap进行排序,遍历输出
  • 看不太懂对吧......是的,暴力破解法思路很暴力,但是这个代码搞不来
                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();
  • 还是看一下更符合该题意的简单方式吧,利用二叉搜索树的性质。
  • 因为是二叉搜索树,所以中序遍历的话,它是排好序的,去找一个数出现的频率也好找,因为都挨在一起
  • 需要用到的有
class Solution {
    ArrayList<Integer> resList;//记录出现次数为maxCount的节点值
    int maxCount;//不断更新,以找到最多的频率
    int count;//累计节点值出现次数,然后用来与maxCount比较
    TreeNode pre;//记录上一次递归的节点,来比较上一个节点跟现在的节点是否相同
    
  • 对列表的更新操作
        if (count > maxCount) {//找到了新的出现频率高的值
            resList.clear();//清空列表
            resList.add(rootValue);//更新列表
            maxCount = count;//更新最大值
        } else if (count == maxCount) {
            resList.add(rootValue);//其他的众数(出现频率一致
        }
  • 还要不断更新pre节点
  • 其他的就是递归套路,中序遍历的递归套路,和上一题差不多的样子

最近公共祖先

普通二叉树:236

  • 初步思路,就是利用哈希表,把节点继承关系记录下来,然后不断往上找,利用哈希集合找重复元素,可行
  • 递归思路
  • 求最小公共祖节点,从下往上,是一个回溯,而二叉树的后序遍历是一个天然回溯
  • 函数返回值和参数:需要返回值,以告诉我们是否找到了节点,而且不仅要找到特定节点,最后结果还是要找它们的公共节点,故返回值为节点。原函数即可
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q)
  • 结束条件:找到特定节点(p q)或者到了头,就返回
        if (root == null || root == p || root == q) { // 递归结束条件
            return root;
        }
  • 单层递归逻辑:用两个返回量 left right来确定当前节点的左右子树是否有特定节点。如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解

如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然

        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;
        }

搜索二叉树:235

  • 感觉和上一个可以用同一套代码,但是它是搜索二叉树,应该有更好的
  • 根据搜索二叉树的特性:在遍历二叉搜索树的时候就是寻找区间[p.val, q.val] 那么如果 cur.val 大于 p.val,同时 cur.val 大于q.val,那么就应该向左遍历(说明目标区间在左子树上)。如果cur.val在这个区间内,说明p q两节点在它的左右子树,那么cur确实就是p q的最近公共祖节点

需要注意的是此时不知道p和q谁大,所以两个都要判断

二叉搜索树中的插入操作

题目:701

  • 递归返回值和参数:可以利用返回值完成新加入的节点与其父节点的赋值操作
public TreeNode insertIntoBST(TreeNode root, int val)
  • 终止条件:终止条件就是找到遍历的节点为null的时候,就是要插入节点的位置了,并把插入的节点返回。
if (root == null) // 如果当前节点为空,也就意味着val找到了合适的位置,此时创建节点直接返回。
            return new TreeNode(val);
  • 单层递归:利用二叉搜索树的性质,决定是去搜索左子树还是右子树。感觉单层递归可以就以第一层递归为例,毕竟每一层都这样,第一层还好想
        if (root.val < val){
            root.right = insertIntoBST(root.right, val); // 递归创建右子树
        }else if (root.val > val){
            root.left = insertIntoBST(root.left, val); // 递归创建左子树
        }
        return root;//递归后的结果,第一层里的结果就是要返回根节点