The thief discovered a new area to steal from. There is only one entrance to this area, which we call it root
. In root
addition, each house has one and only one "parent" house connected to it. After some reconnaissance, the clever thief realized that "all the houses in this place were arranged like a binary tree." If two directly connected houses are robbed on the same night , the house will automatically call the police. Given a binary tree . Returns the maximum amount a thief can steal without triggering an alarm . root
>>Ideas and analysis
- The maximum amount of money a node can get by "stealing" or "not stealing" can be recorded and calculated in real time.
- Dynamic programming uses a state transfer container to record state changes, and uses an array of length 2 to record the maximum money obtained bythe current node "stealing" or "not stealing"
- Perform state transfer on the tree , using recursive trilogy + dynamic five-part process
>>Recursive Trilogy + Dynamic Five Trilogy
1. Determine the parameters and return value of the recursive function
The return value is a dp array with a length of 2 , used to record the money obtained by the node "stealing" and "not stealing"
vector<int> robTree(TreeNode* cur) {
...
}
- dp[0] records the maximum money obtained by the node "without stealing"
- dp[1] records the maximum money obtained by "stealing" the node
Common doubts (O_O)? How can a dp array of length 2 mark the status of each node in the tree?
[Answer] In fact, during the recursive process, the system stack will save the parameters of each level of recursion~
2. Determine the termination conditions
During the traversal process, if an empty node is encountered, whether "stealing" or "not stealing" is 0 , so {0,0} is returned directly. Because the monetary benefit of an empty node is 0. And this is equivalent to the initialization of the dp array
if(cur == NULL) return vector<int>{0,0};
3. Determine the traversal order
- Explicitly use post-order traversal because the return value of the recursive function needs to be used for the next calculation.
- By recursing the left node , we can get the "stealed" and "not stolen" money of the left node.
- By recursing the right node , the money "stealed" and "not stolen" of the right node can be obtained
4. Determine the logic of single-level recursion
- If you "steal" the current node, then the left and right children "cannot steal" , val1 = cur -> val + left[0] + right[0];
- If the current node is "not stolen" , then the left and right children can "steal" . Choose the largest one among the "stealing" and "not stealing" records of the left and right children , that is, val2 = max(left[0],left[1]) + max(right[0],right[1]);
- Finally, the status of the current node is {val2, val1}; (meaning "not stealing" the maximum money obtained by the current node, "stealing" the maximum money obtained by the current node)
5. Use an example to derive the dp array
Finally, the return value of max{result[0],result[1]}; is the maximum amount of money stolen by the head node.
class Solution {
public:
// 长度为2的数组,0:不偷,1:偷
vector<int> robTree(TreeNode* cur) {
if(cur == NULL) return vector<int>{0,0};
vector<int> left = robTree(cur->left);
vector<int> right = robTree(cur->right);
// 偷 cur,那么就不能偷左右节点
int val1 = cur->val + left[0] + right[0];
// 不偷 cur,那么可以偷也可以不偷左右节点,则取较大的情况
int val2 = max(left[0],left[1]) + max(right[0],right[1]);
// cout << "{val2 , val1} :" << val2 << "," << val1 <<endl;
return {val2,val1};
}
int rob(TreeNode* root) {
vector<int> result = robTree(root);
return max(result[0],result[1]);
}
};
- Time complexity: O(n) , each node is only traversed once
- Space complexity: O(log n) , including the space of the recursive system stack
Reference and recommended articles and videos
Code Random Record (programmercarl.com)
Class screenshots from Code Caprice: