LeetCode 热题 HOT 100 -------198.打家劫舍(动态规划) 337. 打家劫舍 III(动态规划)

dsds在这里插入图片描述

//首先想到动态规划,如果只有一间房,那么只能偷这一间房的,如果有两间房,则怎样,以此类推
//边界条件:dp[0]=0,dp[1]=nums[0],dp[2]=max(nums[0],nums[1])以此到dp[n]间房
//转移方程:dp[n] = max[dp[[n-2]+nums[i],dp[n-1];
class Solution {
    public int rob(int[] nums) {
        int len = nums.length;
        int[] dp = new int[len];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[1], nums[0]);
        for(int i = 2 ; i < len ; i++){
            dp[i] = Math.max(dp[i-2] + nums[i] , dp[i-1]);
        }
        return dp[len-1];
    }
}

dsds在这里插入图片描述

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */

 //第一种就是暴力递归,看解析中我们想成爷爷,两个孩子,四个孙子来说明问题
 //偷钱有两种偷法:爷爷偷+4个孙子偷,只有2个儿子偷
 //则单个节点的计算 : 爷爷和孙子偷的:int money1 = root.val+rob(root.left.left)+rob(root.left.right)+rob(root.right.left)+rob(root.left.right),儿子偷的:int money2 = rob(root.left) + rob(root.right);选择钱最多的方案int result  = Math.max(money1,money2)
// 1、虽然能解出来,但是递归时间复杂度太高,所以我们进行优化
// class Solution {
//     public int rob(TreeNode root) {
//         //递归头
//         if(root == null){
//             return 0;
//         }
//         int money = root.val;
//         if(root.left!=null){
//             money += rob(root.left.left) + rob(root.left.right);
//         }
//         if(root.right!=null){
//             money += rob(root.right.left) + rob(root.right.right);
//         }
//         return Math.max( money , rob(root.left) + rob(root.right));
//     }
// }

// 2、对第一种方法进行优化,有一个重复的子问题:当爷爷算钱的时候,孙子的也会算,那么当到了爸爸节点的时候,依旧要算孙子的
// 解决方法:之前的问题都是使用数组解决的,现在把每次计算的结果都存起来,下次如果再来计算,就从缓存中取,不再计算了,这样就保证每个数只计算一次,由于二叉树不适合拿数组当缓存,我们这次使用哈希表来存储结果,TreeNode 当做 key,能偷的钱当做 value

// class Solution {

//     public int rob(TreeNode root ) {
//         //通过哈希表进行存储每个节点的最大值
//        Map<TreeNode , Integer> moneyMaxPerNode = new HashMap<>();
//        return robInternal(root , moneyMaxPerNode);
//     }
//     public int robInternal(TreeNode root , Map<TreeNode , Integer> moneyMaxPerNode){
//         //递归头
//         if(root == null){
//             return 0;
//         }
//         //避免多次存入,相当于对方法一的剪枝
//         if(moneyMaxPerNode.containsKey(root)){
//             return moneyMaxPerNode.get(root);
//         }
//         //当前值
//         int money = root.val;
//         //和孙子的值
//         if(root.left!=null){
//             money += (robInternal( root.left.left, moneyMaxPerNode)+ robInternal(root.left.right , moneyMaxPerNode));
//         }
//         if(root.right != null){
//             money += (robInternal(root.right.left , moneyMaxPerNode) + robInternal(root.right.right , moneyMaxPerNode));
//         }
//         //爷爷+孙子 和 儿子的值的比较
//         money = Math.max(money , robInternal(root.left,moneyMaxPerNode) + robInternal(root.right , moneyMaxPerNode));
//         //存入最大值
//         moneyMaxPerNode.put(root , money);
//         return money;
//     }
// }

// //方法3:虽然我们通过一个哈希表进行了优化,但是也有性能的损耗,换一种方法来表示:使用一个大小为 2 的数组来表示 int[] res = new int[2] 0 代表不偷,1 代表偷
// //1、当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
// //2、当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
// //对应公式:
// //root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val
// //root[0] = Math.max(rob(root.left)[0],rob(root.left)[1]) + Math.max(rob(root.right)[0],rob(root.right)[1])

class Solution {
    public int rob(TreeNode root) {
        //分成两种情况,分别放到数组中,然后取其中的最大值
        int[] maxMoney = robInternal(root);
        return Math.max(maxMoney[0] , maxMoney[1]);
    }
    public int[] robInternal(TreeNode root){
        if(root == null){
            return new int[2];
        }
        int[] choiceArray = new int[2];
        
        int[] left = robInternal(root.left);
        int[] right = robInternal(root.right);

        choiceArray[0] = Math.max(left[0], left[1]) + Math.max(right[0] ,right[1]);
        choiceArray[1] = left[0] + right[0] + root.val;

        return choiceArray;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值