leetcode-括号的分数

63 阅读1分钟

题目描述

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

最开始时:

  • 「一号」玩家从 [1, n] 中取一个值 x(1 <= x <= n);
  • 「二号」玩家也从 [1, n] 中取一个值 y(1 <= y <= n)且 y != x。

「一号」玩家给值为 x 的节点染上红色,而「二号」玩家给值为 y 的节点染上蓝色。

之后两位玩家轮流进行操作,「一号」玩家先手。每一回合,玩家选择一个被他染过色的节点,将所选节点一个 未着色 的邻节点(即左右子节点、或父节点)进行染色(「一号」玩家染红色,「二号」玩家染蓝色)。

如果(且仅在此种情况下)当前玩家无法找到这样的节点来染色时,其回合就会被跳过。

若两个玩家都没有可以染色的节点时,游戏结束。着色节点最多的那位玩家获得胜利 ✌️。

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

示例 1:

示例1.png

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

示例 2:

输入: root = [1,2,3], n = 3, x = 1
输出: false

思路

本题的示例1有一定的误导性,当如上的二叉树,一号玩家先手选择3号的情况下,二号玩家的最优解并不是选择2号,而是选择1号,这样1号以及1号的左子树都会归二号玩家着色。

image.png

跟着这个特例,我们推广到更加一般的情况,看看二号玩家的选择策略。其实一号玩家先手选择了一个节点x后,可以把二号玩家可选的范围分成3部分:

  • x的左子树
  • x的右子树
  • 其他

image.png 其实每个部分都只有1个节点跟x直接相邻,分别是x的左子节点、右子节点、父节点。控制住这个节点,相当于控制住了这整个部分,因为通往这部分剩余的节点必须经过这个节点,而规则明确只能选择邻节点进行染色(不包含兄弟节点)。而选择这个节点控制住这个部分后,由于通往另外2个部分必须经过节点x,所以剩下的区域都是归属一号玩家控制,能否取胜就看这部分包含的节点数是否能大于 n / 2

那么到代码细节,我们首先要找到一号玩家选择的节点x,由于节点的值都不同,可以通过层序遍历二叉树来查找,查询到节点x后,再count出左子树、右子树的节点数,第3部分“其他”的节点数使用 n - 1 - left -right 计算得出。如果这3块区域,有1块区域的节点数大于n / 2,那么就可以让二号玩家取胜,否则一号玩家取胜。

Java版本代码

class Solution {
    public boolean btreeGameWinningMove(TreeNode root, int n, int x) {
        TreeNode xNode = find(root, x);
        int left = count(xNode.left);
        if (left > n / 2) {
            return true;
        }
        int right = count(xNode.right);
        if (right > n / 2) {
            return true;
        }
        int other = n - 1 - left - right;
        if (other > n / 2) {
            return true;
        }
        return false;
    }

    /**
     * 找到值为val的节点
     * @param node 当前遍历的节点
     * @param val 需要寻找的节点的值
     * @return 值为val的节点
     */
    private TreeNode find(TreeNode node, int val) {
        if (node == null) {
            return null;
        }
        if (node.val == val) {
            return node;
        }
        TreeNode next = find(node.left, val);
        if (next != null) {
            return next;
        } else {
            return find(node.right, val);
        }
    }

    /**
     * 当前节点node及它的子节点的节点数量
     * @param node 当前节点
     * @return 节点数量
     */
    private int count(TreeNode node) {
        if (node == null) {
            return 0;
        }
        return 1 + count(node.left) + count(node.right);
    }
}

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天,点击查看活动详情