Home
avatar

Kaixun

Top 150 · 一维动态规划(5 题)

线性 DP:爬楼梯、打家劫舍、零钱兑换与 LIS。

本模块共 5 题,属于 LeetCode 面试经典 150 题 系列。

70. 爬楼梯

难度: 简单

力扣做题

思路

dp[i]=dp[i1]+dp[i2]dp[i] = dp[i-1] + dp[i-2]

代码

class Solution {
    public int climbStairs(int n) {
        if(n == 1){
            return 1;
        }
        int[] stairs = new int[n];
        stairs[0] = 1;
        stairs[1] = 2;
        for (int i = 2; i < n; i++) {
            stairs[i] = stairs[i-1] + stairs[i-2];
        }
        return stairs[n-1];
    }
}

复杂度

  • 时间:O(n)O(n)
  • 空间:O(n)O(n)

备注


198. 打家劫舍

难度: 中等

力扣做题

思路

dp[i]=max(dp[i2]+nums[i],max[i1]),代表当前最多偷多少dp[i] = max(dp[i-2]+nums[i],max[i-1]),代表当前最多偷多少

代码

class Solution {
    public int rob(int[] nums) {
        int[] max = new int[nums.length];
        int len = nums.length;
        if(len == 1){
            return nums[0];
        }
        if(len == 2){
            return Math.max(nums[0],nums[1]);
        }
        max[0] = nums[0];
        max[1] = Math.max(nums[0],nums[1]);
        for (int i = 2; i < len; i++) {
            max[i] = Math.max(max[i-2]+nums[i],max[i-1]);
        }
        return max[len-1];
    }
}

复杂度

  • 时间:O(n)O(n)
  • 空间:O(n)O(n)

备注


139. 单词拆分

难度: 中等

力扣做题

思路

dp[i] 代表前i个可以用字典中的表达

代码

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int len = s.length();
        boolean[] dp = new boolean[len+1];
        dp[0] = true;
        for (int i = 0; i < len; i++) {
            if(dp[i]){
                for (int j = 0; j < wordDict.size(); j++) {
                    if(i+ wordDict.get(j).length() > len){
                        continue;
                    }
                    if(s.startsWith(wordDict.get(j),i)){
                        dp[i+wordDict.get(j).length()] = true;
                    }
                }
            }
        }
        return dp[len];
    }
}

复杂度

  • 时间:O(n2)O(n^2)
  • 空间:O(n)O(n)

备注


322. 零钱兑换

难度: 中等

力扣做题

思路

dp[i]代表金额i可以用最少几个coin表示

代码

class Solution {
    public int coinChange(int[] coins, int amount) {
        int n = coins.length;
        int[] dp = new int[amount+1];
        dp[0] = 0;
        for (int i = 1; i <= amount; i++) {
            int min = Integer.MAX_VALUE;
            for (int j = 0; j < n; j++) {
                if(i-coins[j] <0 || dp[i - coins[j]] == Integer.MAX_VALUE) continue;
                min = Math.min(dp[i-coins[j]]+1,min);
            }
            dp[i] = min;
        }
        return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];
    }
}

复杂度

  • 时间:O(amountn)O(amount \cdot n)
  • 空间:O(amount)O(amount)

备注


300. 最长递增子序列

难度: 中等

力扣做题

思路

dp[i]代表到自己的递增子序列有几个

代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n = nums.length;
        if(n ==1){
            return 1;
        }
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int ans = 1;
        for (int i = 1; i < n; i++) {
            int max =1;
            for (int j = 0; j < i; j++) {
                if(nums[j] < nums[i]){
                    max = Math.max(max, dp[j] + 1);
                }
            }
            dp[i] = max;
            ans = Math.max(dp[i] , ans);
        }
        return ans;
    }
}

复杂度

  • 时间:O(n2)O(n^2)
  • 空间:O(n)O(n)

备注

二分+贪心的nlogn做法

维护一个数组tails,tails[i]表示长度为i+1的递增子序列的最小结尾元素

每次来一个新数x:x比tails所有数都大 → 直接追加,长度+1;否则 → 二分找到第一个>=x的位置,替换掉

class Solution {
public int lengthOfLIS(int[] nums) {
    int[] tails = new int[nums.length];
    int size = 0; // tails的有效长度
    for (int x : nums) {
        // 二分找第一个>=x的位置
        int left = 0, right = size;
        while (left < right) {
            int mid = (right-left)/2+left;
            if(tails[mid]< x){
                left = mid +1;
            }else{
                right = mid;
            }
        }
        tails[left] = x; // 替换或追加
        if (left == size) size++; // 追加了新元素
    }
    return size;
}
}

Top150 动态规划 一维DP