树型DP(上)
树型DP在树结构上求解最优子结构,常用于选点、路径等问题,状态在子树间转移,自底向上递推。
树型DP(上)
树型DP(上)
最大BST子树
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
class TreeNode {
public:
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Info {
public:
// 整个树的最大节点值
long max;
// 整个树的最小节点值
long min;
// 整棵树是否是 BST
bool isBst;
// 整棵树上的最大的 BST 的大小
int maxBstSize;
Info(long a, long b, bool c, int d) : max(a), min(b), isBst(c), maxBstSize(d) {}
};
class Solution {
public:
int largestBSTSubtree(TreeNode *root) {
return fc(root).maxBstSize;
}
private:
// 后序遍历
Info fc(TreeNode *node) {
// 防止空节点干扰父节点是否是 BST 的判断
// 左空节点的最大值为 LONG_MIN 才能满足小于父节点 int 值
if (node == nullptr)
return Info(LONG_MIN, LONG_MAX, true, 0);
Info lInfo = fc(node->left);
Info rInfo = fc(node->right);
long max_ = std::max((long) node->val, std::max(lInfo.max, rInfo.max));
long min_ = std::min((long) node->val, std::min(lInfo.min, rInfo.min));
bool isBst = lInfo.isBst && rInfo.isBst && lInfo.max < node->val && node->val < rInfo.min;
int maxBSTSize;
if (isBst) {
maxBSTSize = lInfo.maxBstSize + rInfo.maxBstSize + 1;
} else {
maxBSTSize = std::max(lInfo.maxBstSize, rInfo.maxBstSize);
}
// 返回父节点可能用到的所有信息
return Info(max_, min_, isBst, maxBSTSize);
}
};
int main() {
/*
构造如下二叉树:
10
/ \
5 15
/ \ \
1 8 7
最大的 BST 子树是:
5
/ \
1 8
所以最大 BST 子树大小是 3。
*/
TreeNode *root = new TreeNode(10);
root->left = new TreeNode(5);
root->right = new TreeNode(15);
root->left->left = new TreeNode(1);
root->left->right = new TreeNode(8);
root->right->right = new TreeNode(7);
Solution sol;
int result = sol.largestBSTSubtree(root);
cout << "最大BST子树的大小是: " << result << endl;
// 清理内存
delete root->left->left;
delete root->left->right;
delete root->left;
delete root->right->right;
delete root->right;
delete root;
return 0;
}
1373. 二叉搜索子树的最大键值和
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
49
50
51
52
53
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
struct Info {
int minVal;
int maxVal;
bool isBST;
// 树的累加和,与是否是 BST 无关
int sum;
int maxBSTSum;
Info(int minVal, int maxVal, bool isBST, int sum, int maxBSTSum) : minVal(minVal), maxVal(maxVal), isBST(isBST),
sum(sum),
maxBSTSum(maxBSTSum) {}
};
class Solution {
public:
int maxSumBST(TreeNode *root) {
return fc(root).maxBSTSum;
}
Info fc(TreeNode *root) {
if (root == nullptr) return Info(INT_MAX, INT_MIN, true, 0, 0);
Info l = fc(root->left);
Info r = fc(root->right);
int minVal = min(root->val, min(l.minVal, r.minVal));
int maxVal = max(root->val, max(l.maxVal, r.maxVal));
bool isBST = l.isBST && r.isBST && (l.maxVal < root->val) && (root->val < r.minVal);
int sum = l.sum + r.sum + root->val;
int maxBSTSum = max(l.maxBSTSum, r.maxBSTSum);
if (isBST) maxBSTSum = max(sum, maxBSTSum);
return Info(minVal, maxVal, isBST, sum, maxBSTSum);
}
};
543. 二叉树的直径
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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
struct Info {
int height;
int maxPath;
Info(int h, int m) : height(h), maxPath(m) {}
};
class Solution {
public:
int diameterOfBinaryTree(TreeNode *root) {
return fc(root).maxPath;
}
Info fc(TreeNode *root) {
if (root == nullptr) return Info(0, 0);
Info l = fc(root->left);
Info r = fc(root->right);
int height = max(l.height, r.height) + 1;
int maxPath = max(l.maxPath, r.maxPath);
maxPath = max(maxPath, l.height + r.height);
return Info(height, maxPath);
}
};
979. 在二叉树中分配硬币
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
49
50
51
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
struct Info {
// 节点总数
int count;
// 硬币总数
int coins;
// 移动步数
int move;
Info(int ct, int c, int m) : count(ct), coins(c), move(m) {}
};
class Solution {
public:
int distributeCoins(TreeNode *root) {
return fc(root).move;
}
Info fc(TreeNode *root) {
if (root == nullptr) return Info(0, 0, 0);
Info l = fc(root->left);
Info r = fc(root->right);
int count = l.count + r.count + 1;
int coins = l.coins + r.coins + root->val;
// l.move 是指将 l 的左子树的硬币和左子树节点个数匹配,且 l 的右子树硬币和右子树节点个数匹配,所需要的步数
// 尽管移动完后,l 节点的硬币数可能是正可能是负(根节点操作完刚好是 1)
// abs(l.count - l.coins) 是指 l 向其父节点送去硬币或取回硬币,使得 l 节点刚好有一个硬币,操作完后,l 子树的每个节点都有一个硬币
// r 也同理
int move = l.move + r.move + abs(l.count - l.coins) + abs(r.count - r.coins);
return Info(count, coins, move);
}
};
337. 打家劫舍 III
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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
struct Info {
// 选这个节点的情况下的最高金额
int pick;
// 不选这个节点的情况下的最高金额
int noPick;
Info(int a, int b) : pick(a), noPick(b) {}
};
class Solution {
public:
int rob(TreeNode *root) {
Info i = fc(root);
// 最终结果是在当前节点选或不选的两种情况下二选一
return max(i.pick, i.noPick);
}
Info fc(TreeNode *root) {
if (root == nullptr) return Info(0, 0);
Info l = fc(root->left);
Info r = fc(root->right);
// 选中当前节点,那么子节点一定不能选
int pick = root->val + l.noPick + r.noPick;
// 不选当前节点,子节点可选可不选
int noPick = max(l.pick, l.noPick) + max(r.pick, r.noPick);
return Info(pick, noPick);
}
};
968. 监控二叉树
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
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
int res;
int minCameraCover(TreeNode *root) {
res = 0;
if (fc(root) == 0) res++;
return res;
}
// 0: root 是无覆盖的状态,root 下方的节点都已经被覆盖
// 1: root 是覆盖状态,root 上没摄像头,root 下方的节点都已经被覆盖
// 2: root 是覆盖状态,root 上有摄像头,root 下方的节点都已经被覆盖
int fc(TreeNode *root) {
if (root == nullptr) return 1;
int l = fc(root->left);
int r = fc(root->right);
if (l == 0 || r == 0) {
res++;
return 2;
}
if (l == 1 && r == 1) return 0;
return 1;
}
};
437. 路径总和 III
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
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
class Solution {
public:
int res;
int pathSum(TreeNode *root, int sum) {
res = 0;
// 保存前缀树,key 为前缀和,value 为前缀和出现的次数
unordered_map<long, int> preSum;
// 前缀树为 0 的个数至少是一个
preSum[0] = 1;
fc(root, sum, 0, preSum);
return res;
}
void fc(TreeNode *root, int target, long sum, unordered_map<long, int> &preSum) {
if (root == nullptr) return;
// 从头节点到 x 的累加和
sum += root->val;
// 若是存在前缀和为 prefixSum - target 的节点,则该节点到当前节点的路径就是符合题意的
res += preSum[sum - target];
// 当前的前缀和的出现次数加一
preSum[sum]++;
fc(root->left, target, sum, preSum);
fc(root->right, target, sum, preSum);
// 子树都已经遍历完了,从 map 中去掉当前节点的前缀和,使得兄弟结点无法使用当前结点的前缀和
preSum[sum]--;
}
};
本文由作者按照 CC BY 4.0 进行授权