大概题意:给定一棵二叉树,每个节点都有一个值,要求从其中选取若干个节点,是的值的和最大,约束条件为任意两个选取的节点之间不能相连。
解题的思路也很清晰,我们可以用动态规划的做法,以递归的方法实现。对于任意一个父节点,我们有取与不取两种选择:如果取,则其两个子节点都不能取;如果不取,则其两个子节点即可取也可以不取。
一开始我实现了一个函数来辅助实现。函数为 int max_rob(TreeNode* root, int t) ,其中参数t 用来表示当前节点是否取:1为取,0为不取。具体实现如下:
class Solution {
public:
int max_rob(TreeNode* root, int t) {
if (root == NULL) return 0 ;
int mrl0 = max_rob(root->left, 0) ;
int mrr0 = max_rob(root->right, 0) ;
if (t == 1) return root->val + mrl0 + mrr0 ;
else {
return max(max_rob(root->left, 1), mrl0) + max(max_rob(root->right, 1), mrr0 )) ;
}
}
int rob(TreeNode* root) {
return max(max_rob(root, 1), max_rob(root, 0) ) ;
}
};
然而这样的写法是TLE的,不能通过最后一个测试样例。观察写法,虽然已经用了两个局部变量mrl0 和 mrr0 来记录两个子节点不取的情况下的各自的最大值了,但在最差的情况下,即t = 1 的情况下,每个节点都要对其两个子节点遍历两次,相当于遍历4个节点。如果树的层数为n,则时间复杂度就为O(4^(n+1))。
所以我们需要更高效的实现。能不能再遍历每个节点的时候就把取了它和不取它的最大值都求出来呢?是可以的。具体实现如下:
class Solution {
public:
pair<int, int> max_rob(TreeNode* root) {
if (root == NULL) return 0 ;
pair<int, int> lans = max_rob(root -> left) ;
pair<int, int> rans = max_rob(root -> right) ;
pair<int, int> ans ;
ans.first = root->val + lans.second + rans.second ;
ans.second = max(lans.first, lans.second) + max(rans.first, rans.second) ;
return ans ;
}
int rob(TreeNode* root) {
pair<int, int> ans = max_rob(root) ;
return max(ans.first, ans.second) ;
}
};
这种写法,每个节点只需遍历其两个子节点各一次,如果树的层数为n,则时间复杂度为O(2^(n+1)),这种实现方法的时间复杂度远远小于上一种写法。
这道题对我的启发与其说是算法上,不如说是在写法上。不同的写法可能会出现差异很大的时间复杂度。