二叉树基础(四)

11 篇文章 0 订阅

617.合并二叉树

题意:给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

image-20220602105108234

1、思路:递归法

  • 本题使用哪种遍历都是可以的!
    • 确定递归函数的参数和返回值:TreeNode mergeTrees(TreeNode root1, TreeNode root2),参数传入两个树的根节点
    • **确定终止条件:**传入两棵树,遍历时就会存在t1和t2两个节点,当t1为空时,直接返回t2,当t2为空时,直接返回t1
    • **确定单层递归的逻辑:**可以借助t1重复利用,也可以重新创建一个树TreeNode root = new TreeNode(root1.val + root2.val);,并依次合并左子树和右子树
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        //DFS 递归
        if (root1 == null) return root2;
        if (root2 == null) return root1;

        TreeNode root = new TreeNode(root1.val + root2.val);

        root.left = mergeTrees(root1.left, root2.left);
        root.right = mergeTrees(root1.right, root2.right);
        return root;
    }

2、思路:迭代法

public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
    //BFS 迭代
    if (root1 == null) return root2;
    if (root2 == null) return root1;
    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root1);
    queue.offer(root2);

    while (!queue.isEmpty()) {
        TreeNode node1 = queue.poll();
        TreeNode node2 = queue.poll();
        //只处理两个节点都不为空的情况
        node1.val = node1.val + node2.val;
        //如果两棵树的左节点都不为空,则加入队列中
        if (node1.left != null && node2.left != null) {
            queue.offer(node1.left);
            queue.offer(node2.left);
        }
        //如果两棵树的右节点都不为空,则加入队列中
        if (node1.right != null && node2.right != null) {
            queue.offer(node1.right);
            queue.offer(node2.right);
        }
        //如果node1的左节点为空,node2的左节点不为空
        if (node1.left == null && node2.left != null) {
            node1.left = node2.left;
        }
        //如果node1的右节点为空,node2的右节点不为空
        if (node1.right == null && node2.right != null) {
            node1.right = node2.right;
        }
    }
    return root1;
}

700.二叉搜索树中的搜索

题意:给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

image-20220606102916104

1、思路:递归法

  • 二叉搜索树是一个有序树:
    • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    • 它的左、右子树也分别为二叉搜索树
  • 普通二叉树方式
  • 确定递归函数的参数和返回值:TreeNode searchBST(TreeNode root, int val),参数传入二叉树根节点和要查找的值
  • 确定终止条件:如果当前节点等于空或者当前节点的值等于要查找的值,就直接返回当前节点
  • 确定单次递归的逻辑:
    • 先递归左子树,如果左子树上存在该值,就直接返回节点
    • 如果左子树不存在,则递归右子树,并直接返回右子树
    public TreeNode searchBST(TreeNode root, int val) {
        //DFS 递归 普通二叉树方式
        //如果当前节点等于null,则直接返回该节点,或者当前节点不等于null,当前节点的值等于val
        if (root == null || root.val == val) return root;

        TreeNode left = searchBST(root.left, val);
        if (left != null) {
            return left;
        }

        return searchBST(root.right, val);
    }
  • 针对二叉搜索树
  • 递归函数的参数和返回值,终止条件都相同
  • 对于单次递归逻辑来说,因为二叉搜索树是有序的,所以可以判断当前节点值和要查找值之间关系,如果当前节点值小于要查找值,则直接查找右子树并返回,否则就直接查找左子树并直接返回
    public TreeNode searchBST(TreeNode root, int val) {
        //DFS 递归 二叉搜索树
        //如果当前节点等于null,则直接返回该节点,或者当前节点不等于null,当前节点的值等于val
        if (root == null || root.val == val) return root;

        if (root.val > val) {
            return searchBST(root.left, val);
        }else {
            return searchBST(root.right, val);
        }
    }

2、思路:迭代法

  • 利用栈实现前序遍历,当碰到当前节点的值与查找的值相等时,直接返回当前节点
    public TreeNode searchBST(TreeNode root, int val) {
        //DFS 迭代 前序 普通二叉树
        if (root == null) return null;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);

        while (!stack.isEmpty()) {
            TreeNode node = stack.pop();

            if (node != null && node.val == val) {
                return node;
            }

            if (node.left != null) stack.push(node.left);
            if (node.right != null) stack.push(node.right);

       }
       return null;
    }
  • 二叉树遍历的迭代法,可能立刻想起使用栈来模拟深度遍历,使用队列来模拟广度遍历
  • 对于二叉搜索树可就不一样了,因为二叉搜索树的特殊性,也就是节点的有序性,可以不使用辅助栈或者队列就可以写出迭代法。
  • 循环判断当前节点是否为null,在循环体中判断当前节点值与查找值是否相等
public TreeNode searchBST(TreeNode root, int val) {
    //DFS 迭代 前序 搜索二叉树
    while (root != null) {
        if (root.val == val) return root;

        if (root.val > val) root = root.left;
        else root = root.right;
    }
    return null;
}

98.验证二叉搜索树

题意:给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

image-20220606142253534

1、思路:递归法

  • 第一种方法:采用中序遍历,转变成一个数组,只需要判断这个数组是否有序
  • 第二种方法:不需要创建数据,直接比较,同样需要采用中序遍历
    • 确定递归函数和返回值,boolean isValidBST(TreeNode root)
    • 确定终止条件,if (root == null) return true;
    • 确定单次递归的逻辑
      • 利用中序进行递归遍历左右子树
      • 在中间节点进行处理,当max节点不为空并且当前节点的值小于等于max节点的值,直接返回false
      • 无论max节点是否为空,都将当前节点赋值给max,max主要用来记录上一个节点
    private TreeNode max;

    public boolean isValidBST(TreeNode root) {
        //DFS 递归  不能单纯比较左右节点与当前节点的大小
        //需要采用中序遍历
        if (root == null) return true;
        //左
        boolean left = isValidBST(root.left);
        if (!left) return false;
        //中
        if (max != null && max.val >= root.val) return false;
        max = root;
        //右
        boolean right = isValidBST(root.right);
        return right;
    }

2、思路:迭代法

  • 中序迭代法,没采用统一迭代法
  • 创建一个栈,循环判断当前节点不为空或者栈不为空
    • 当当前节点不为空,就一直往左节点遍历
    • 中间节点处理逻辑与递归类似,如果max节点不为空并且当前节点的值小于max节点,就直接返回false
public boolean isValidBST(TreeNode root) {
    //DFS 迭代法
    Stack<TreeNode> stack = new Stack<>();
    TreeNode node = root;

    TreeNode max = null;
    while (node != null || !stack.isEmpty()) {
        //左
        while (node != null) {
            stack.push(node);
            node = node.left;
        }
        //中
        TreeNode pop = stack.pop();
        if (max != null && pop.val <= max.val) return false;
        max = pop;
        //右
        node = pop.right;
    }
    return true;
}

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

题意:给你一棵所有节点为非负值的二叉搜索树,请你计算树中任意两节点的差的绝对值的最小值。

image-20220606145702206

1、思路:递归法

  • 因为是二叉搜索树,所以最小绝对差一定是中序遍历的相邻两个值
  • 采用中序遍历
    • 确定递归的函数和返回值,void getMinimum(TreeNode root),参数传入根节点
    • 确定终止条件,当当前节点为空,直接返回
    • 确定单次递归的逻辑
      • 首先递归左子树
      • 对中间节点进行处理,当前继节点不为空,比较当前节点与前继节点值的差值与结果比价,并将较小值赋给结果
      • 递归右子树
    private int result = Integer.MAX_VALUE;
 	private TreeNode pre;
    public int getMinimumDifference(TreeNode root) {
        //DFS 递归 中序
        if (root == null) return 0;
        getMinimum(root);
        return result;
    }

    private void getMinimum(TreeNode root) {
        if (root == null) return;

        //左
        getMinimum(root.left);
        //中
        if (pre != null) result = Math.min(result, root.val - pre.val);
        pre = root;
        //右
        getMinimum(root.right);
    }

2、思路:迭代法

  • 采用中序迭代法
  • 其余逻辑与中序递归法一样
public int getMinimumDifference(TreeNode root) {
    //DFS 迭代法 中序
    if (root == null) return 0;
    Stack<TreeNode> stack = new Stack<>();

    TreeNode node = root;
    TreeNode pre = null;
    int result = Integer.MAX_VALUE;
    while (node != null || !stack.isEmpty()) {
        //左
        if (node != null) {
            stack.push(node);
            node = node.left;
        } else {
            //中
            TreeNode pop = stack.pop();
            if (pre != null) result = Math.min(result, pop.val - pre.val);
            pre = pop;
            //右
            node = pop.right;
        }
    }
    return result;
}

501.二叉搜索树中的众数

题意:给定一个有相同值的二叉搜索树(BST),找出 BST 中的所有众数(出现频率最高的元素)。

假定 BST 有如下定义:

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

image-20220606150957408

1、思路:递归法

  • 普通二叉树方式
  • 采用任何遍历顺序都可以,采用前序遍历,并用map记录每个数出现的次数
  • 重点在于如果对Map的value进行排序
    • 通过map.entrySet()获取map的entryset集合
    • 将entrySet转换成流,调用流的sorted方法,对map中的value进行排序
    • 排序完成调用collect(Collectors.toList())转换成List集合
    public int[] findMode(TreeNode root) {
        //DFS 递归法  遍历整个二叉树 当成普通二叉树来操作
        Map<Integer, Integer> map = new HashMap<>();
        List<Integer> list = new ArrayList<>();
        if (root == null) return list.stream().mapToInt(Integer::intValue).toArray();

        findMode1(root, map);
        List<Map.Entry<Integer, Integer>> mapList = map.entrySet().stream().sorted(new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o2.getValue() - o1.getValue();
            }
        }).collect(Collectors.toList());
        list.add(mapList.get(0).getKey());
        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();
    }

    private void findMode1(TreeNode node, Map<Integer, Integer> map) {
        if (node == null) return;
        //前序遍历
       map.put(node.val, map.getOrDefault(node.val, 0) + 1);
        findMode1(node.left, map);
        findMode1(node.right, map);
    }
  • 二叉搜索树
  • 必须使用中序遍历,因为中序遍历是递增数组
  • 定义前继节点pre、当前值出现的次数count,以及出现的最大次数maxCount
  • 主要在于中间节点的处理逻辑
    • 如果前继节点为空或着前继节点的值不等于当前节点的值,count赋值为1,否则count++
    • 如果count大于maxCount,那么就将结果集情况,并将当前节点值存放到结果集中,maxCount修改为count,如果count与maxCount相等,将当前节点值存放到结果集中
    • 前继节点pre修改为当前节点
    ArrayList<Integer> list;
    private TreeNode pre;
    private int count;
    private int maxCount;

    public int[] findMode(TreeNode root) {
        //DFS 递归法  遍历整个二叉树 当成搜索二叉树来操作
        //中序遍历
        if (root == null) return null;
        list = new ArrayList<>();
        count = 0;
        maxCount = 0;
        findMode1(root);
        return list.stream().mapToInt(Integer::intValue).toArray();
    }

    private void findMode1(TreeNode root) {
        if (root == null) return;

        //左
        findMode1(root.left);
        //中
        if (pre == null || pre.val != root.val) {
            count = 1;
        } else {
            count++;
        }
        //更新结果
        if (count > maxCount) {
            list.clear();
            list.add(root.val);
            maxCount = count;
        } else if (count == maxCount) {
            list.add(root.val);
        }
        pre = root;

        findMode1(root.right);
    }

2、思路:迭代法

  • 与递归法完全一样
public int[] findMode(TreeNode root) {
    //DFS 迭代法
    if (root == null) return null;

    ArrayList<Integer> list = new ArrayList<>();
    TreeNode pre = null;
    int count = 0;
    int maxCount = 0;
    TreeNode node = root;
    Stack<TreeNode> stack = new Stack<>();
    while (node != null || !stack.isEmpty()) {
        if (node != null) {
            stack.push(node);
            node = node.left;
        } else {
            node = stack.pop();
            //中
            if (pre == null || pre.val != node.val) {
                count = 1;
            } else {
                count++;
            }
            //更新结果
            if (count > maxCount) {
                list.clear();
                list.add(node.val);
                maxCount = count;
            } else if (count == maxCount) {
                list.add(node.val);
            }
            pre = node;
            node = node.right;
        }
    }
    return list.stream().mapToInt(Integer::intValue).toArray();
}

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

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

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

image-20220606153122428

1、思路:递归法

  • 首先想的是要是能自底向上查找就好了,这样就可以找到公共祖先了
  • 后序遍历
    • 确定递归函数返回值以及参数:TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q),参数传入树的根节点、两个节点p和q
    • 确定终止条件:如果找到了 节点p或者q,或者遇到空节点,就返回。
    • 确定单次递归的逻辑:
      • 需要遍历整个树,想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。
      • 如果left 和 right都不为空,说明此时root就是最近公共节点。这个比较好理解
      • 如果left为空,right不为空,就返回right,说明目标节点是通过right返回的,反之依然
      • 如果left和right都为空,就直接返回空

image-20220606154604901

package com.yzu.lee.treenode;

/**
 * @ClassName: LowestCommonAncestor
 * @Description:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
 * 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x
 * ,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
 * @author: Leekuangyew
 * @date: 2022/5/30 16:19
 */
public class LowestCommonAncestor {
    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) return null;
        else if (left == null && right != null) return right;
        else if (right == null && left != null) return left;
        else return root;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leekuangyee

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值