[路飞]_程序员必刷力扣题: 968. 监控二叉树

206 阅读4分钟

968. 监控二叉树

给定一个二叉树,我们在树的节点上安装摄像头。

节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。

计算监控树的所有节点所需的最小摄像头数量。

示例 1:

image.png

输入: [0,0,null,0,0]
输出: 1
解释: 如图所示,一台摄像头足以监控所有节点。

示例 2:

image.png

输入:[0,0,null,0,null,0,null,null,0]
输出:2
解释:需要至少两个摄像头来监视树的所有节点。 上图显示了摄像头放置的有效位置之一。

提示:

  1. 给定树的节点数的范围是 [1, 1000]
  2. 每个节点的值都是 0。

贪心+递归+状态转移

思路

要在树中安装摄像头来监控到所有的树

遍历整棵树我们可以有两种方式,从上到下,或者从下到上

那么我们肯定希望每一个摄像头都能监控到更多的节点

遍历方式

  • 从上到下遍历,那么可能最后一层的爷爷节点都安装了摄像头的话,叶子节点就都需要安装摄像头,肯定不是最少的摄像头

所以我们要从下到上遍历

  • 让每个叶子节点的父节点安装摄像头,就能保证每颗摄像头都能覆盖尽量多的节点,从而使监视整棵树的节点数量最少

找到所有节点状态

每个节点的所有状态:

  • 当前节点没有被覆盖,用0表示
  • 当前节点有摄像头,用1表示
  • 当前节点被覆盖,用2表示
  • 当前节点没有摄像头(没有摄像头就是被覆盖,或者未被覆盖,上面三种状态已经包含了该状态)

当然我们还会遇到空节点null,比如叶子节点(子树都为null)的子节点为两个null

那么这个null节点的状态是什么呢:

  • null没有被覆盖:那么我们就需要给null节点的父节点安装摄像头,这样就相当于给叶子节点安装摄像头,很明显不符合题意,数量肯定不是最少的
  • null节点有摄像头:不符合题意,不能给null安装摄像头
  • null节点被覆盖:那么null的父节点(叶子节点)就是未覆盖的状态,这时候我们就可以给null的爷爷节点(叶子节点的父节点)安装摄像头,满足我们的要求

如果某非叶子节点的子树为null:

  • null没有被覆盖:那么单边的树上没一个节点都需要安装摄像头来覆盖null,很明显不正确
  • null安装摄像头:不符合题意
  • null节点被覆盖:此时不用考虑该null节点,只需要再覆盖其父节点即可

所以null节点的状态只能是被覆盖

并且找到状态转移方程

  • 情况1: 左右子树至少有一个未覆盖0 left=0 right = 0

left=0 right = 1

left=0 right = 2

left=1 right = 0

left=2 right = 0

此时当前节点必然为未覆盖状态1

if(left === noCover||right===noCover){
    res++
    return isCamera
}
  • 情况2:左右子树至少有一个安装摄像头

left=1 right=0

left=1 right=1

left=1 right=2

left=0 right=1

left=2 right=1

那么当前节点必然为已覆盖状态2

if(left === isCamera || right === isCamera){
    return isCover
}
  • 情况3:左右子树都被覆盖 left=1 right=1
if(left === isCover && right === isCover){
    return noCover
}

那么当前节点就是无覆盖0

以上就是所有的情况了,每个节点3中状态,两个节点共9种状态,也可以跟据当前节点的3中状态,去反推出所有子节点的状态,道理是一样的

至此,我们已经找到三种状态方程

注意: 如果我们根节点的状态为未覆盖0,我们还需要在根节点安装一个摄像头

if(traversal(root)===noCover) res++
var minCameraCover = function (root) {
    var noCover = 0
    var isCover = 1
    var isCamera = 2
    var res = 0
    var traversal = function(root){
        if(root===null) return isCover

        var left = traversal(root.left)
        var right = traversal(root.right)

        if(left === noCover||right===noCover){
            res++
            return isCamera
        }
        if(left === isCamera || right === isCamera){
            return isCover
        }
        if(left === isCover && right === isCover){
            return noCover
        }
    }
    if(traversal(root)===noCover) res++
    return res

};