LeetCode100题持续更新中

1、动态规划

1.1 爬楼梯 (70)

题目
在这里插入图片描述

分析:f[i] 表示从第1层爬到第i层阶梯有多少种方法,i = 1…n。
状态转移方程:f[i] = f[i-1] + f[i-2]
理解:最后一步有两种可能,可能爬 1 阶,也可能爬 2阶,如果爬1阶,子问题为 f[i-1];如果爬2阶,子问题为 f[i-2]; 都有可能,所以加起来。
初始化: f[0] = 1, f[1] = 1;要凑 f[2] = 2,所以 f[0] = 1

class Solution {
   
   
    public int climbStairs(int n) {
   
   

        int[] ans = new int[n+1];
        ans[0]  = 1;
        ans[1]  = 1;
        for(int i = 2;i<= n;i++)
        {
   
   
            ans[i] = ans[i-1] +ans[i-2];
        }
        return ans[n];

    }
}

1.2 杨辉三角 (118)

题目
在这里插入图片描述
注意:numRows>=1;

分析
List<List<Integer>> 这是返回类型,选取数据结构很重要,要遍历输出,选取HashMap<Integer,<List<Integer>>> 作为存储结构。其中List<Integer> 选取ArrayList<>()作为存储结构。然后,注意到边缘位置全是1,然后非边缘位置 l[i] 是上一层 l[i-1] + l[i]。
代码

class Solution {
   
   
    public List<List<Integer>> generate(int numRows) {
   
   
      
      Map<Integer,List<Integer>> hp = new HashMap<>();

      List<List<Integer>> ans = new ArrayList<>();

      for(int i = 1;i<=numRows;i++)
      {
   
   
        List<Integer> l = new ArrayList<>();
        for(int j = 0;j<i;j++)// 第i行恰好有i个元素,开始赋值
        {
   
   
            if(j == 0 || j == i-1)
            l.add(1);
            else
            {
   
   
                // 拿到上一层的list
                List<Integer> le = hp.get(i-1);
                // 例如:l[2] = le[1]+le[2]
                l.add(le.get(j-1)+le.get(j));
            }
        }
        hp.put(i,l);// 只是为记忆上一层
        ans.add(l);

      }
      return ans;

    }
}

1.3 打家劫舍 (198)

题目
在这里插入图片描述
分析
非负,这是一个很关键信息,因为这样表示状态时可以用前 i 个元素的最大目标值作为状态;不难分析到这是一个选与不选问题,状态转移方程如下:
d p [ i ] = m a x ( d p [ i − 2 ] + n u m s [ i ] , d p [ i − 1 ] ) \mathrm{ dp[i] = max(dp[i-2]+nums[i],dp[i-1])} dp[i]=max(dp[i2]+nums[i],dp[i1])
其中 d p [ i ] \mathrm{dp[i]} dp[i] 表示的是下标为 0 - i 元素的最大目标值; d p [ 0 ] = n u m s [ 0 ] \mathrm{dp[0] = nums[0]} dp[0]=nums[0]

代码

class Solution {
   
   
    public int rob(int[] nums) {
   
   
        
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        if(nums.length>1)// 原因 从2开始,防止越界
        dp[1] = Math.max(nums[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];

   


        
    }
}

1.4 完全平方数 (279)

题目
在这里插入图片描述
分析:关键是如何确定一个数可以被平方数+数来表示,进而转换为子问题,状态转移方程有:
d p [ i ] = m i n ( d p [ i ] , d p [ i − j ∗ j ] + 1 ) \mathrm{dp[i]=min(dp[i],dp[i-j*j]+1)} dp[i]=min(dp[i],dp[ijj]+1)
其中 d p [ i ] \mathrm{dp[i]} dp[i]表示的是数字 i 最少可以用几个平方数来表示, i − j ∗ j ≥ 0 \mathrm{i-j*j\ge0} ijj0, dp[0] = 0。
代码

class Solution {
   
   
    public int numSquares(int n) {
   
   
        

        // dp[0] = 0,是因为如果一个数据本身就是平方数 dp[i-j*j]=0, 即 dp[i-j*j]+1 = 1
        int[] dp = new int[n+1];
        dp[0] = 0;

        for(int i = 1;i<=n;i++ )
        {
   
   
            dp[i] = i;//最大值,尽量避免使用Integer.MAX_VALUE
            for(int j = 0;j*j<=i;j++)
            {
   
   
                dp[i] = Math.min(dp[i],dp[i-j*j]+1); 
            }

        }
        return dp[n];

    }
}

1.5 零钱兑换 (322)

题目在这里插入图片描述
分析
如何定义状态变量,转换成子问题,才是关键,要想计算 dp[amount] ,不难想到其子问题是 dp[amount-coin] ,当然,对于每一个硬币coin都要进行相减,所以状态转移方程有:
d p [ i ] = m i n ( d p [ i ] , d p [ i − c o i n ] + 1 ) \mathrm{dp[i] = min(dp[i],dp[i-coin]+1)} dp[i]=min(dp[i],dp[icoin]+1)
其中 dp[i] 表示能组成金额i的最小硬币数量,dp[0] = 0.
代码

class Solution {
   
   
    public int coinChange(int[] coins, int amount) {
   
   
        
        int[] dp  = new int[amount+1];
        dp[0] =  0;// 表示金额0肯定是0 个硬币数量
        
        for(int i =1;i<=amount;i++)
        {
   
   
            dp[i] = amount+1;// 这是一个不可能的最大值
            for(int coin: coins)
            {
   
   
                if(i>=coin)
                {
   
   
                    dp[i] = Math.min(dp[i],dp[i-coin]+1);
                }
            }

        }
        if(dp[amount] == amount+1)
        {
   
   
            return  -1;
        }
        else
        {
   
   
            return dp[amount];
        }
        
    }
}

1.6 单词划分 (139)

题目在这里插入图片描述
分析
要转换为子问题,先定义好一个状态变量(父问题),再找子问题,dp[i] 表示的是 s[0] - s[i-1] 能否被字典划分 , i = 1,2,3…s.length(), 对于每一个状态定义都要严丝合缝; 寻找子问题, 对于字典中的每一个单词, 都要看 s[i-word.length()] - s[i-1] 是否满足是该单词, 而且dp[i-word.length()]为true, 也就是说 s[0] - s[i-word.length()-1] 可以被划分; 都满足, OK 令 dp[i] = true. 状态转移方程为:
d p [ i ] = i − w o r d . l e n g t h ( ) ≥ 0 ∧ d p [ i − w o r d . l e n g t h ( ) ] ∧ w o r d = = s u b ( s ) \mathrm{dp[i] = i-word.length()\ge0 \wedge dp[i-word.length()] \wedge word == sub(s) } dp[i]=iword.length()0dp[iword.length()]word==sub(s)

其中 dp[ 0] = true, 原因就是如果sub(s)本来就是从头开始, dp[0] = true 可以不影响我们对该字串的判断.
代码:

class Solution {
   
   
    public boolean wordBreak(String s, List<String> wordDict) {
   
   

        // 从 i=1 开始判断  dp[i] 表示 s[0]-s[i-1] 能否 被划分
        // dp[0] = true;

        // dp[1] 表示 s[0] 能否划分

        // dp[i] =  [dp[i-word.length] &&  s(i-word.length: i-1) == word] or (nextword)
  
        int s_len = s.length();
        boolean[] dp = new boolean[s_len+1];
        dp[0] = true;
        for(int i = 1;i<=s_len;i++)
        {
   
   
            for(String word : wordDict)
            {
   
   
                // l e e t c ode  dp[4] 为例
                // 0 1 2 3 4      dp
                int leftIndex = i - word.length();// 当前可能匹配单词的起始左下标
                if(leftIndex>=0 && dp[leftIndex] && word.equals(s.substring(leftIndex,i)))
                {
   
   
                    dp[i] = true;
                    break;//  只要找到一个就可以       
                }
            }
        }

        return dp[s_len];



        
    }
}

1.7 最长递增子序列 (300)

题目
在这里插入图片描述
分析
定义状态变量dp[i] 表示 以 下标 i 为结尾的最长子序列长度, 子问题就是,如果当前 nums[i] 大于 nums[j] , j = 0…i-1 , dp[i] = max( dp[i] , dp[j] + 1)
代码

class Solution {
   
   
    public int lengthOfLIS(int[] nums) {
   
   

        //i    0  1 2 3 4 5
        //num  4,10,4,3,8,9
        //dp   1  2 2 2 3 4 
        //  如果dp[i] 0-i元素的最大值子序列长度,我们会发现会出错,dp[4] = 3,因为8虽然大于3,dp[3] = 2,
        //  但是dp[4]显然等于2,因为dp[3] = 2,不是以3结尾的最长长度,所以不能用dp[i]表示0-i元素的最大值子序列长度
        
        // dp[i] 表示 以 i 为结尾的最长子序列长度

        int[] dp = new int[nums.length];
        dp[0] = 1;
        int maxlen = 1;
        for(int i = 1;i<nums.length;i++)
        {
   
   
            dp[i] = 1; // 如果nums[i] 无法和之前元素构成更长子序列,那么dp[i] = 1
           
            for(int j = 0;j<i;j++)// 因为它可能和所有0-----i-1 构成更长子序列
            {
   
   
                if(nums[i]>nums[j])
                {
   
   
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }

            }
            if(dp[i]>maxlen)
            maxlen = dp[i];
        }
        return maxlen;
  
    }
}

1.8 乘积最大子数组 (152)

题目
在这里插入图片描述
分析
最大从哪里来,这是化解为子问题的关键,当然,题目变为最小也一样。审题:非空连续。 maxdp[i] 代表以下标i为结尾的最大值,只和nums[i],maxdp[i-1],mindp[i-1] 有关。
例如: -2 1 -3 dp[2] = 6 要用到mindp[1] = -2,所以要记录最小也要记录下来。
状态转移方程为:
m a x d p [ i ] = m a x ( d [ i ] , m a x d p [ i − 1 ] ∗ n u m s [ i ] , m i n d p [ i − 1 ] ∗ n u m s [ i ] ) \mathrm{maxdp[i] = max(d[i],maxdp[i-1]*nums[i],mindp[i-1]*nums[i])} maxdp[i]=max(d[i],maxdp[i1]nums[i],mindp[i1]nums[i])
代码

class Solution {
   
   
    public int maxProduct(int[] nums) {
   
   
        
 

    int n = nums.length;
    int[] maxdp = new int[n];
    int[] mindp = new int[n];
    
    maxdp[0] = nums[0];   
    mindp[0] = nums[0];
    int max  = nums[0];
    for<
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值