83.打家劫舍

74 阅读2分钟

题目链接

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

解法1 记忆化递归

思路

这题和跳楼梯的思路类似,分解子问题就是从左到右采取“选和不选”的方式,即选第 i 个房子时,要和 i - 2 相加(因为不能连续),然后和 i - 1 比较最大值。

递推公式即 robFrom(i) = max(robFrom(i - 1), robFrom(i - 2) + nums[i])

代码

function rob(nums: number[]): number {
    const n = nums.length;
    const memo = new Array(n).fill(-1);

    const robFrom = (i) => {
        if (i < 0) return 0;
        if (memo[i] !== -1) return memo[i];

        memo[i] = Math.max(robFrom(i - 1), nums[i] + robFrom(i - 2));
        return memo[i];
    };
    return robFrom(n - 1);
};

时空复杂度

时间复杂度:O(n)

空间复杂度:O(n)

解法2 动态规划和空间优化

思路

动态规划的版本其实与记忆化递归类似,递推公式都是一样的。

空间优化呢主要是因为在计算过程中都只用到了 dp[i - 1]dp[i - 2] 这两个变量,所以我们就用两个变量来代替整个 dp 数组。

prev:表示 dp[i-2],即抢到 i-2 个房子的最大金额。

cur:表示 dp[i-1],即抢到 i-1 个房子的最大金额。

即抢当前房子:prev + num,不抢当前房子:cur

代码

动态规划

function rob(nums: number[]): number {
    const n = nums.length;
    const dp = new Array(n).fill(0);
    dp[0] = nums[0];
    dp[1] = Math.max(dp[0], nums[1]);

    for (let i = 2; i < n; i++) {
        dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
    }

    return dp[n - 1];
};

空间优化

function rob(nums: number[]): number {
    let prev = 0;
    let cur = 0;

    for (let num of nums) {
        const temp = cur;
        cur = Math.max(prev + num, cur);
        prev = temp;
    }

    return cur;
};

时空复杂度

时间复杂度:都是 O(n)

空间复杂度:动态规划 O(n),空间优化 O(1)