问题描述

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1

给定 n ,请计算 F(n) 。

示例 1:

输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

示例 2:

输入:n = 3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2

示例 3:

输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

提示:

  • 0 <= n <= 30

核心思路

核心思路

局部最优:让叶子节点的父节点安摄像头,所用摄像头最少。

整体最优:全部摄像头数量所用最少。

从下到上,先给叶子节点父节点放个摄像头,然后隔两个节点放一个摄像头,直至到二叉树头结点。

实现要点

  1. 二叉树的遍历

    使用后序遍历也就是左右中的顺序,在回溯的过程中从下到上进行推导。

  2. 如何隔两个节点放一个摄像头

    每个节点可能有如下三种状态,空节点的状态只能是有覆盖,才能在叶子节点的父节点放摄像头:

    • 0:该节点无覆盖
    • 1:本节点有摄像头
    • 2:本节点有覆盖

    单层逻辑处理有如下四类情况:

    • 情况1:左右节点都有覆盖

    中间节点应该是无覆盖的状态。

    • 情况2:左右节点至少有一个无覆盖的情况

    摄像头数量+1,并且return 1,代表中间节点放摄像头。

    • 情况3:左右节点至少有一个有摄像头

    其父节点就应该是2(覆盖的状态)。

    • 情况4:头结点没有覆盖

      递归结束之后,还要判断根节点,如果没有覆盖,result++。

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
private:
int result;
int traversal(TreeNode* cur) {

// 空节点,该节点有覆盖
if (cur == NULL) return 2;

int left = traversal(cur->left); // 左
int right = traversal(cur->right); // 右

// 情况1
// 左右节点都有覆盖
if (left == 2 && right == 2) return 0;

// 情况2
// left == 0 && right == 0 左右节点无覆盖
// left == 1 && right == 0 左节点有摄像头,右节点无覆盖
// left == 0 && right == 1 左节点有无覆盖,右节点摄像头
// left == 0 && right == 2 左节点无覆盖,右节点覆盖
// left == 2 && right == 0 左节点覆盖,右节点无覆盖
if (left == 0 || right == 0) {
result++;
return 1;
}

// 情况3
// left == 1 && right == 2 左节点有摄像头,右节点有覆盖
// left == 2 && right == 1 左节点有覆盖,右节点有摄像头
// left == 1 && right == 1 左右节点都有摄像头
// 其他情况前段代码均已覆盖
if (left == 1 || right == 1) return 2;

// 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
// 这个 return -1 逻辑不会走到这里。
return -1;
}

public:
int minCameraCover(TreeNode* root) {
result = 0;
// 情况4
if (traversal(root) == 0) { // root 无覆盖
result++;
}
return result;
}
};
  • 时间复杂度: O(n)O(n),需要遍历二叉树上的每个节点。

  • 空间复杂度: O(n)O(n)