leetcode-337:打家劫舍 III

简介: leetcode-337:打家劫舍 III

题目

题目链接

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]
     3
    / \
   2   3
    \   \ 
     3   1
输出: 7 
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:

输入: [3,4,5,1,3,null,1]
     3
    / \
   4   5
  / \   \ 
 1   3   1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

解题

方法一:动态规划

参考链接

每个节点有两个状态 1.偷该节点 2.不偷该节点, 用一个长度为2的数组表示

要使用后续遍历,这样才可以通过左右节点的返回值,去做下一步的计算。

if(!cur) return vector{0,0};就可以作为初始值

class Solution {
public:
    int rob(TreeNode* root) {
        vector<int> res=robTree(root);
        return max(res[0],res[1]); 
    }
    // 长度为2的数组,0:不偷,1:偷
    vector<int> robTree(TreeNode* cur){
        if(!cur) return vector<int>{0,0};
        vector<int> left=robTree(cur->left);
        vector<int> right=robTree(cur->right);
        int val1=max(left[0],left[1])+max(right[0],right[1]);//不偷cur
        int val2=cur->val+left[0]+right[0];//偷cur
        return {val1,val2};
    }
};

将vector换成array后效率更高

class Solution {
public:
    int rob(TreeNode* root) {
        array<int,2> res=robTree(root);
        return max(res[0],res[1]); 
    }
    // 长度为2的数组,0:不偷,1:偷
    array<int,2> robTree(TreeNode* cur){
        if(!cur) return {0,0};
        array<int,2> left=robTree(cur->left);
        array<int,2> right=robTree(cur->right);
        int val1=max(left[0],left[1])+max(right[0],right[1]);//不偷cur
        int val2=cur->val+left[0]+right[0];//偷cur
        return {val1,val2};
    }
};

java

class Solution {
    public int rob(TreeNode root) {
        int[] res=robTree(root);
        return Math.max(res[0],res[1]);
    }
    int[] robTree(TreeNode cur){
        if(cur==null) return new int[]{0,0};
        int[] left=robTree(cur.left);
        int[] right=robTree(cur.right);
        int val1=Math.max(left[0],left[1])+Math.max(right[0],right[1]);
        int val2=cur.val+left[0]+right[0];
        return new int[]{val1,val2};
    }
}

方法二:暴力递归(超时)

暴力递归,可以发现,当前根节点,调用了

class Solution {
public:
    int rob(TreeNode* root) {
        if(!root) return 0;
        if(!root->left&&!root->right) return root->val;
        int val1=root->val;//偷当前节点
        if(root->left) val1+=rob(root->left->left)+rob(root->left->right);
        if(root->right) val1+=rob(root->right->left)+rob(root->right->right);
        int val2=rob(root->left)+rob(root->right);//不偷当前节点
        return max(val1,val2);
    }
};

当然以上代码超时了,这个递归的过程中其实是有重复计算了。

我们计算了root的四个孙子(左右孩子的孩子)为头结点的子树的情况,又计算了root的左右孩子为头结点的子树的情况,计算左右孩子的时候其实又把孙子计算了一遍。

方法三:记忆化递归

在方法二的基础上,使用unordered_map记录访问过的root,避免重复访问,减少时间复杂度

class Solution {
public:
    unordered_map<TreeNode*,int> umap;//添加的地方
    int rob(TreeNode* root) {
        if(!root) return 0;
        if(!root->left&&!root->right) return root->val;
        if(umap[root]) return umap[root];//添加的地方
        int val1=root->val;
        if(root->left) val1+=rob(root->left->left)+rob(root->left->right);
        if(root->right) val1+=rob(root->right->left)+rob(root->right->right);
        int val2=rob(root->left)+rob(root->right);
        umap[root]=max(val1,val2);//添加的地方
        return max(val1,val2);
    }
};


相关文章
|
1月前
|
Go
golang力扣leetcode 337.打家劫舍III
golang力扣leetcode 337.打家劫舍III
28 0
|
1月前
|
Go
golang力扣leetcode 198.打家劫舍
golang力扣leetcode 198.打家劫舍
23 0
|
1月前
leetcode代码记录(打家劫舍 III
leetcode代码记录(打家劫舍 III
19 0
|
1月前
力扣337.打家劫舍3(树形dp)
力扣337.打家劫舍3(树形dp)
|
1月前
力扣198.打家劫舍(简单动态规划)
力扣198.打家劫舍(简单动态规划)
|
1月前
leetcode代码记录(打家劫舍 II
leetcode代码记录(打家劫舍 II
16 3
|
1月前
leetcode代码记录(打家劫舍
leetcode代码记录(打家劫舍
18 1
|
1月前
力扣213打家劫舍2(简单动态规划)
力扣213打家劫舍2(简单动态规划)
|
1月前
|
Java
leetcode-198:打家劫舍
leetcode-198:打家劫舍
28 0
leetcode-198:打家劫舍
|
1月前
|
算法 vr&ar 图形学
☆打卡算法☆LeetCode 198. 打家劫舍 算法解析
☆打卡算法☆LeetCode 198. 打家劫舍 算法解析