【LeetCode 手撕算法】(动态规划)爬楼梯、杨辉三角、打家劫舍、完全平方数、零钱兑换、单词拆分、最长递增子序列、乘积最大子数组、分割等和子集

模版:

java 复制代码
判空
特殊条件
dp[n] = dp[n-1] + dp[n-2]
遍历dp
条件判断
取值返回

70-爬楼梯

**思路:**设置初始值1和2位置的,然后算3

**注意:**返回值里n代表索引,不是数组长度;

数组长度要设置为n+1,多一个,方便和i对齐;

java 复制代码
class Solution {
    public int climbStairs(int n) {
        int[] dp=new int[n+1];//0到n ,总共n+1个
        if(n<2){return n;}
        dp[1]=1;
        dp[2]=2;
        for(int i=3;i<n+1;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

118-杨辉三角

思路:建二维的,先设尖尖初始值,一层层处理,然后添加进结果; 层处理时,前后添加1,中间值为上一层的j-1j的和

**注意:**判空条件;

添加用add(xxx),获取用 get(i);

注意层里元素的位置和层数,可用遍历的 i变形来表示出来

java 复制代码
class Solution {
    public List<List<Integer>> generate(int numRows) {
       
        List <List<Integer>> result=new ArrayList<>(); //建二维列表
        
        if(numRows==0){return result;}//判空
       
        List<Integer> firstRows=new ArrayList<>(); //设初始值塔尖
        firstRows.add(1);
        result.add(firstRows);
        
        for(int i=1;i<numRows;i++){//遍历层数,第1行有了,从第2行开始
            List<Integer>curRows=new ArrayList<>(); //当前层
            List<Integer>preRows=result.get(i-1); //上一层,并获取层的值.get()
            curRows.add(1);//开头加一
            //中间 上一层左右之和
            for(int j=1;j<i;j++){
                int num=preRows.get(j-1)+preRows.get(j);
                curRows.add(num);
            }
            
            curRows.add(1); //结尾加一
            result.add(curRows);
        }
        return result;
    }
}

198-打家劫舍

**思路:**遍历到i时,两种情况i-1可以偷也可以不偷,选个最大(dp[i-1] ,dp[i-2]+nums[i]) ,dp存 的是每个位置最大金额数

注意: 特殊情况和边界,nums为0为1;比较大小用Math.max( )

java 复制代码
class Solution {
    public int rob(int[] nums) {
        //特殊情况
        if(nums==null||nums.length==0){return 0;}
        if(nums.length==1){return nums[0];}
        int [] dp=new int[nums.length];
        dp[0]=nums[0];
        dp[1]=Math.max(dp[0],nums[1]);
        for(int i=2;i<nums.length;i++){
            dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
        }
        return dp[nums.length-1];
    }
}

279-完全平方数

**思路:**每次取前一个(减去一个平方数)的值+1 ,判断和现在的平方数谁更少

**注意:**dp[ 0 ] 不参与使用;

考虑特殊情况,全为1;

注意条件边界选n还是n+1

java 复制代码
class Solution {
    public int numSquares(int n) {
        int []dp=new int[n+1];//从1开始,0空下,对应上i索引
        //最坏情况全由1组成
        for(int i=1;i<n+1;i++){
            dp[i]=i;
        }
        //遍历每个i,i的上一个是 减一个平方数再加1
        for(int i=1;i<n+1;i++){
            for(int j=1;j*j<=i;j++){
                dp[i]=Math.min(dp[i],dp[i-j*j]+1);//比较上一个再+1
            }
        }
        return dp[n];
    }
}

322-零钱兑换

思路:dp存硬币数,先把所有的ap初始值设成报错形式(amount+1) ;遍历dp,遍历硬币,只要硬币能凑齐 就返回少一个硬币的最小值。 使用**min(dp[ i ] ,dp[ i -coin] +1 )**更新dp,并设置条件防止dp[负数]的出现。

**注意:**dp初始值要设成最大的,且报错形式;

由于i-coin 遍历过程中可能出现负数,要做coin<= i 的预防;

结果只判断amount位置的金额,没错就返回,有错(即初始值)就返-1

java 复制代码
class Solution {
    public int coinChange(int[] coins, int amount) {
        int [] dp=new int[amount+1];//dp表示每个金额所需硬币数量
        for(int i=0;i<amount+1;i++){
            dp[i]=amount+1;   //要进行min,初始值设置最大值(不易实现就行,要比amount大,方便返回报错)
        }
        //每次减一个硬币参与比较
        dp[0]=0;
        for(int i=1;i<amount+1;i++){
            for(int coin:coins){
                if(coin<=i){ //可进行(即硬币小于金额 避免出现dp里有负的)
                    dp[i]=Math.min(dp[i],dp[i-coin]+1);
                }
            }
        }
        if(dp[amount]>amount){  //把初始值设置成报错形式 dp都为amount+1
            return -1;
        }else{
            return dp[amount];
        }
    }
}

139-单词拆分

**思路:**遍历dp,存i之前的是否在字典里;判断方法,取i从j开始判断(j之前都对,且j到i之间也在字典里),没问题就标记dp[ i ],下一个

**注意:**输入是list 有重复,设成set形式保证不重复;

set的用 .contains( ) ,其他的用get()

java 复制代码
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordSet=new HashSet<>(wordDict);//把wordDict变成set形式好操作,元素不重复
        boolean []dp =new boolean[s.length()+1];
        dp[0]=true;
        for( int i=1;i<=s.length();i++){ //取s字符串的长度用length()
            for(int j=0;j<i;j++){
                if(dp[j]==true&&wordSet.contains((s.substring(j,i)))){
                    dp[i]=true; //j以前的都存在,j到i字典也包含,则没毛病
                    break; //没问题就下一个
                }
            }
        }
        return dp[s.length()];
    }
}

300-最长递增子序列

**思路:**三板斧:判空特殊条件、设初始值、遍历dp[ i ]和j

本题条件 要找到每个位置 前面按升序的数字个数,遍历每个位置为结束点,再从头遍历数

有几个,dp[ i ]dp[ j ]+1进行比较并更新最大的

**注意:**这次dp长度直接nums就行,0到length-1就可以索引;

i不用从i=0开始,从i=1开始就行,0的位置本身就算一个

java 复制代码
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums==null||nums.length==0){return 0;}//特殊情况
        int []dp=new int [nums.length];
        Arrays.fill(dp,1);//给dp所有内容变成1
        int max=1;
        for(int i=1;i<nums.length;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                    dp[i]=Math.max(dp[i],dp[j]+1);//更新dp[i]
                }
            }
            max=Math.max(dp[i],max);
        }
        return max;
    }
}

152-乘积最大子数组

**思路:**由于有正负两种情况 之前是最小 下一个就可能最大,所以设置两种max和min,分别对比(当前值、上一最大值*当前值、上一最小值*当前值),持续更新max和min,并比较三者。最大的值暂存就是结果

**注意:**老套路模版,dp和max的初始值要设置成第一个值;

由于只取最大值,所以循环一遍就行。

java 复制代码
class Solution {
    public int maxProduct(int[] nums) {
        if(nums==null||nums.length==0){return 0;}
        int n=nums.length;
        int dpMax=nums[0];
        int dpMin=nums[0];
        int max=nums[0];

        for(int i=1;i<nums.length;i++){
            int premax=dpMax;//将上一轮的dpmax存在premax
            int premin=dpMin;
            dpMax=Math.max(nums[i],Math.max(premax*nums[i],premin*nums[i]));//更新dpmax 选最大
            dpMin=Math.min(nums[i],Math.min(premax*nums[i],premin*nums[i]));
            max=Math.max(dpMax,max);  //取每轮最大值
        }
        return max;
    }   
}

416-分割等和子集

**思路:**包里的石头初始为目标重量,从重量范围内依次减石头,直到减到nums[ 0 ] ,将其也设置成true

例子 1、5、11、5

总和为22,中间目标值为11,从目标重量开始减nums[ i ] ,若出现到dp[ 0 ]初始值√ ,那也将其设置成√; 一轮结束后 ,在继续减5、11、5;

java 复制代码
索引:    0     1     2     3     4     5     6     7     8     9    10    11
dp:     ✅    ❌   ❌   ❌    ❌    ❌   ❌    ❌   ❌   ❌   ❌    ❌

第一轮: 从11开始持续 -1判断

复制代码
索引:    0     1     2      3     4      5     6      7      8     9    10    11
dp:     ✅    ✅    ❌    ❌    ❌    ❌    ❌    ❌    ❌    ❌    ❌    ❌

第二轮:从11开始持续 -5判断

复制代码
索引:    0     1     2      3      4     5     6      7      8      9    10    11
dp:     ✅    ✅    ❌    ❌    ❌    ✅    ✅    ❌    ❌    ❌    ❌    ❌

第三轮:从11开始持续 -11判断

复制代码
索引:    0     1     2      3      4     5     6      7     8      9     10    11
dp:     ✅    ✅    ❌    ❌    ❌    ✅    ✅    ❌    ❌    ❌    ❌    ✅

第四轮:从11开始持续**-5**判断

复制代码
索引:    0     1     2      3      4     5      6     7      8      9    10    11
dp:     ✅    ✅    ❌    ❌    ❌    ✅    ✅    ❌    ❌    ❌    ✅    ✅

**注意:**0-1背包问题,比较抽象,多刷,死记硬背

java 复制代码
class Solution {
    public boolean canPartition(int[] nums) {
        int sum=0;//先算总和
        for(int i=0;i<nums.length;i++){
            sum+=nums[i];
        }
        if(sum%2==1){return false;}//奇数则不可能实现
        int target=sum/2;
        boolean[] dp=new boolean[target+1];
        dp[0]=true;
        for(int i=0;i<nums.length;i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=dp[j]||dp[j-nums[i]];
            }
        }
        return dp[target];
    }
}
相关推荐
瑞行AI1 小时前
一套数据格式框架搞定大模型微调和对齐训练
算法·语言模型
jake·tang1 小时前
深度解析 VESC 参数辨识源码:电阻、电感与磁链
arm开发·c++·嵌入式硬件·算法·数学建模·傅立叶分析
weelinking1 小时前
2026年三大主流大模型深度对比:GPT-5.5、Claude 4.6与DeepSeek V4谁更值得选择?
java·大数据·人工智能·git·python·gpt·github
图码1 小时前
矩阵边界遍历:顺时针与图案打印的两种高效解法
数据结构·python·线性代数·算法·青少年编程·矩阵·深度优先遍历
sali-tec1 小时前
C# 基于OpenCv的视觉工作流-章72-点-点距离
图像处理·人工智能·opencv·算法·计算机视觉
橘子海全栈攻城狮1 小时前
【最新源码】基于springboot的快递物流平台的设计与实现C102
java·开发语言·spring boot·后端·spring·web安全
m0_739030001 小时前
mabatis-plus 和mabatis 的区别
java·数据库·mybatis
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 小时前
判断两个集合是不是相同
java
凌波粒1 小时前
LeetCode--二叉树层序遍历实战指南
算法·leetcode·职场和发展