树系列问题IV——二叉树

这篇博客主要探讨了与二叉树相关的算法问题,包括二叉树着色游戏的策略、翻转二叉树以匹配先序遍历、查找最大层内元素和、判断单值二叉树、确定翻转等价二叉树、生成所有可能的满二叉树、根据前序和后序遍历构造二叉树、在数据流中找第K大元素以及求解最长同值路径。博客内容详尽解析了各种二叉树问题的解决方案,并展示了高效的Java实现。
摘要由CSDN通过智能技术生成

1145. 二叉树着色游戏
有两位极客玩家参与了一场「二叉树着色」的游戏。游戏中,给出二叉树的根节点 root,树上总共有 n 个节点,且 n 为奇数,其中每个节点上的值从 1 到 n 各不相同。

游戏从「一号」玩家开始(「一号」玩家为红色,「二号」玩家为蓝色),最开始时,
「一号」玩家从 [1, n] 中取一个值 x(1 <= x <= n);
「二号」玩家也从 [1, n] 中取一个值 y(1 <= y <= n)且 y != x。
「一号」玩家给值为 x 的节点染上红色,而「二号」玩家给值为 y 的节点染上蓝色。

之后两位玩家轮流进行操作,每一回合,玩家选择一个他之前涂好颜色的节点,将所选节点一个 未着色 的邻节点(即左右子节点、或父节点)进行染色。
如果当前玩家无法找到这样的节点来染色时,他的回合就会被跳过。
若两个玩家都没有可以染色的节点时,游戏结束。着色节点最多的那位玩家获得胜利 ✌️。

现在,假设你是「二号」玩家,根据所给出的输入,假如存在一个 y 值可以确保你赢得这场游戏,则返回 true;若无法获胜,就请返回 false。

示例:
在这里插入图片描述

输入:root = [1,2,3,4,5,6,7,8,9,10,11], n = 11, x = 3
输出:True
解释:第二个玩家可以选择值为 2 的节点。

提示:
二叉树的根节点为 root,树上由 n 个节点,节点上的值从 1 到 n 各不相同。
n 为奇数。
1 <= x <= n <= 100
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
   
    /*
    首先数字无序,不需要考虑数字顺序;其次,若能保证能够取得一半以上的节点数量,则可赢。因此,设置n个节点的一半为mid,用于后面判断。

    若取父节点,则父节点以上的节点都属于同一个玩家;对于一个节点,若取其左(右)节点,则左(右)节点的所有节点都属于同一个玩家。
    当一号玩家选取了x节点,先计算该节点的左右子树的节点数。可赢的条件为:
        1. 如果left > mid 或者 right > mid, 则只要选取大于一半的那个节点,则必赢
        2. 如果left + right < mid, 选取父节点,则总数超过一半,则必赢
        会输的条件为:
        1. left == mid 或者 right == mid, 无论选取哪个节点,其他另一边节点和父节点都是一号玩家的,一号玩家总数超过一半,都是必输
        2. mid <= left + right < n, 无论选取哪个节点,其他另一边节点和父节点都是一号玩家的,一号玩家总数超过一半,都是必输
    */
    int value;
    int Left;
    int Right;
    public boolean btreeGameWinningMove(TreeNode root, int n, int x) {
   
        value=x;
        nums(root);
        int mid=n/2;
        if(Left>mid || Right>mid || (Left+Right)<mid){
   
            return true;
        }
        return false;
    }
    //计算一号玩家选取x节点后,左右子树的节点数
    public int nums(TreeNode node){
   
        int Leftnumbers=0;
        int Rightnumbers=0;
        if(node.left!=null){
   
            Leftnumbers= nums(node.left);
        }
        if(node.right!=null){
   
            Rightnumbers= nums(node.right);
        }
        if(node.val==value){
   
            Left=Leftnumbers;
            Right=Rightnumbers;
        }
        return Leftnumbers+Rightnumbers+1;
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:36.2 MB, 在所有 Java 提交中击败了55.88%的用户
通过测试用例:65 / 65

971. 翻转二叉树以匹配先序遍历
给你一棵二叉树的根节点 root ,树中有 n 个节点,每个节点都有一个不同于其他节点且处于 1 到 n 之间的值。
另给你一个由 n 个值组成的行程序列 voyage ,表示 预期 的二叉树 先序遍历 结果。

通过交换节点的左右子树,可以 翻转 该二叉树中的任意节点。例,翻转节点 1 的效果如下:
在这里插入图片描述

请翻转 最少 的树中节点,使二叉树的 先序遍历 与预期的遍历行程 voyage 相匹配 。
如果可以,则返回 翻转的 所有节点的值的列表。你可以按任何顺序返回答案。如果不能,则返回列表 [-1]。

示例 1:
在这里插入图片描述

输入:root = [1,2], voyage = [2,1]
输出:[-1]
解释:翻转节点无法令先序遍历匹配预期行程。

示例 2:
在这里插入图片描述

输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
解释:交换节点 2 和 3 来翻转节点 1 ,先序遍历可以匹配预期行程。

示例 3:
在这里插入图片描述

输入:root = [1,2,3], voyage = [1,2,3]
输出:[]
解释:先序遍历已经匹配预期行程,所以不需要翻转节点。
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
   
    /*
    进行深度优先遍历。如果遍历到某一个节点的时候,节点值不能与行程序列匹配,那么答案为 [-1]。否则,当行程序列中的下一个期望数字 voyage[i] 与即将遍历的子节点的值不同的时候,需要翻转一下当前节点。
    */
    List<Integer> list=new ArrayList<>();
    int p;
    int[] voyage;
    public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
   
        p=0;
        this.voyage=voyage;
        DFS(root);
        if(!list.isEmpty() && list.get(0)==-1){
   
            list.clear();
            list.add(-1);
        }
        return list;
    }
    public void DFS(TreeNode node){
   
        if(node!=null){
   
            if(node.val!=voyage[p++]){
   
                list.clear();
                list.add(-1);
                return;
            }
            if(node.left!=null && node.left.val!=voyage[p] && p<voyage.length){
   
                list.add(node.val);
                DFS(node.right);
                DFS(node.left);
            }else {
   
                DFS(node.left);
                DFS(node.right);
            }
        }
    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:38.3 MB, 在所有 Java 提交中击败了79.04%的用户
通过测试用例:104 / 104

1161. 最大层内元素和
给你一个二叉树的根节点 root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。
请你找出层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中 最小 的那个。

示例 1:
在这里插入图片描述

输入:root = [1,7,0,7,-8,null,null]
输出:2
解释:
第 1 层各元素之和为 1,
第 2 层各元素之和为 7 + 0 = 7,
第 3 层各元素之和为 7 + -8 = -1,
所以我们返回第 2 层的层号,它的层内元素之和最大。

示例 2:
输入:root = [989,null,10250,98693,-89388,null,null,null,-32127]
输出:2
解析过程:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
   
    public int maxLevelSum(TreeNode root) {
   
        if(root == null){
   
            return 0;
        }
        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);
        // 设置变量统计当前层数,最大值以及最终结果
        int maxsum=Integer.MIN_VALUE;
        int level=1,res=0;;
        while(!queue.isEmpty()){
   
            int size=queue.size();
            // 在每一层维护一个计算每层元素之和的变量
            int value=0;
            for(int i=0;i<size;i++){
   
                TreeNode cur=queue.poll();
                value+=cur.val;
                if(cur.left!=null){
   
                    queue.offer(cur.left);
                }
                if(cur.right!=null){
   
                    queue.offer(cur.right);
                }
            }
            if(value>maxsum){
   
                maxsum=value;
                res=level;
            }
            level++;
        }
        return res;
    }
}

结果:
执行用时:8 ms, 在所有 Java 提交中击败了83.10%的用户
内存消耗:41.5 MB, 在所有 Java 提交中击败了19.02%的用户
通过测试用例:40 / 40

965. 单值二叉树
如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。
只有给定的树是单值二叉树时,才返回 true;否则返回 false。

示例 1:
在这里插入图片描述

输入:[1,1,1,1,1,null,1]
输出:true

示例 2:
在这里插入图片描述

输入:[2,2,2,5,2]
输出:false
解析过程:
方法一:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
   
    //递归遍历每个节点,判断是否与根节点的值一样
    boolean flag=true;
    public boolean isUnivalTree(TreeNode root) {
   
        int value=0;
        if(root!=null){
   
            value=root.val;
        }
        DFS(root,value);
        return flag;
    }
    public void DFS(TreeNode node,int value){
   
        if(node!=null){
   
            if(node.val!=value){
   
                flag=false;
                return;
            }
            DFS(node.left,value);
            DFS(node.right,value);
        }

    }
}

结果:
执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
内存消耗:35.7 MB, 在所有 Java 提交中击败了92.04%的用户
通过测试用例:72 / 72
方法二:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
   
    //通过先序遍历将所有节点都加入list中,然后判断所有值是否相同
    public boolean isUnivalTree(TreeNode root) {
   
        List<Integer> list=new ArrayList<>();
        Preorder(root,list);
        /*int value=0;
        if(root!=null){
            value=root.val;
        }
        
        for(int i=0;i<list.size();){
            if(list.get(i)==value){
                i++;
            }else{
                return false;
            }
        }
        */
        for(int value:list){
   
            if(value!=list.get(0)){
   
                return false;
            }
        }
        return true;
    }
    public void Preorder(TreeNode node,List<Integer> list){
   
        if(node==null){
   
            return;
        }
        list.add(node.val)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值