Leetcode動態規劃算法題(Java實現)

算法很差,最近準備刷算法題,先從動態規劃開始。


含義

動態規劃(Dynamic programming,簡稱 DP)是一種在數學、管理科學、計算機科學、經濟學和生物信息學中使用的,通過把原問題分解爲相對簡單的子問題的方式求解複雜問題的方法。

動態規劃常常適用於有重疊子問題和最優子結構性質的問題,動態規劃方法所耗時間往往遠少於樸素解法。

動態規劃背後的基本思想非常簡單。大致上,若要解一個給定問題,我們需要解其不同部分(即子問題),再根據子問題的解以得出原問題的解。動態規劃往往用於優化遞歸問題,例如斐波那契數列,如果運用遞歸的方式來求解會重複計算很多相同的子問題,利用動態規劃的思想可以減少計算量。

通常許多子問題非常相似,爲此動態規劃法試圖僅僅解決每個子問題一次,具有天然剪枝的功能,從而減少計算量:一旦某個給定子問題的解已經算出,則將其記憶化存儲,以便下次需要同一個子問題解之時直接查表。這種做法在重複子問題的數目關於輸入的規模呈指數增長時特別有用。


Easy難度

1 最大子序和

在這裏插入圖片描述

	public int maxSubArray(int[] nums) {
        int max=nums[0];
        int temp=nums[0];
        for(int i=1;i<nums.length;i++){
            //當前和爲負數,越加越小,直接使用當前值
            if(temp<0)
                temp=nums[i];
            else 
                temp+=nums[i];
            max=Math.max(max,temp);
        }
        return max;
    }

2 爬樓梯

在這裏插入圖片描述

	public int climbStairs(int n) {
        //經典題目了,爬到n的結果是n-1的結果加上n-2的結果
        if(n==0||n==1||n==2)
            return n;
        int[] dp=new int[n+1];
        dp[0]=0;
        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];
    }

3 買賣股票的最佳時機

在這裏插入圖片描述

	public int maxProfit(int[] prices) {
        if(prices.length==0)
            return 0;
        int max=0;
        int min=prices[0];
        for(int i=1;i<prices.length;i++){
            if(max<prices[i]-min)
                max=prices[i]-min;
            min=Math.min(min,prices[i]);
        }
        return max;
    }

4 打家劫舍

在這裏插入圖片描述

	public int rob(int[] nums) {
        //dp[i]表示偷到第i家的最大金額,
        //根據題意dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1])
        if(nums.length==0)
            return 0;
        if(nums.length==1)
            return nums[0];
        int[] dp=new int[nums.length+1];
        dp[0]=0;//沒有偷,金額0
        dp[1]=nums[0];//偷到第一家,最高金額肯定就是第一家的金額
        for(int i=2;i<nums.length+1;i++){
            //由於dp中加了第0家的概念,因此對應nums[i-1]
            dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
        }
        return dp[nums.length];
    }

5 區域和檢索-數組不可變

在這裏插入圖片描述

class NumArray {

    //sum[i+1]表示nums[0]到nums[i]的和
    private int[] sum;

    public NumArray(int[] nums) {
        sum=new int[nums.length+1];
        for(int i=0;i<nums.length;i++){
            sum[i+1]=sum[i]+nums[i];
        }
    }
    
    public int sumRange(int i, int j) {
        //sumRange(i,j)=sumRange(0,j)-sumRange(0,i-1)
        return sum[j+1]-sum[i];
    }
}

6 判斷子序列

在這裏插入圖片描述

	public boolean isSubsequence(String s, String t) {
        int index=-1;
        for(int i=0;i<s.length();i++){
            char c=s.charAt(i);
            //每次判斷從當前索引的下一個開始,indexOf方法尋找從index+1開始第一個爲c的索引
            index=t.indexOf(c,index+1);
            if(index==-1)
                return false;
        }
        return true;
    }

7 使用最小花費爬樓梯

在這裏插入圖片描述

	public int minCostClimbingStairs(int[] cost) {
        //動態規劃
        int[] dp=new int[cost.length];
        dp[0]=cost[0];
        dp[1]=cost[1];
        for(int i=2;i<cost.length;i++)
            dp[i]=Math.min(dp[i-1],dp[i-2])+cost[i];
        return Math.min(dp[dp.length-1],dp[dp.length-2]);
    }

8 除數博弈

在這裏插入圖片描述

	public boolean divisorGame(int N) {
        //N爲2時,贏,N爲3時,輸
        //因此N爲4時,只要取1個,就可以贏(對方是3,輸)
        //因此N爲偶數可以贏,爲奇數就輸了
        return N%2==0;
    }

9 三步問題

在這裏插入圖片描述
難度不大,主要主要要取模運算

public int waysToStep(int n) {
        int num=1000000007;
        //一樓一種:1,二樓兩種1 1,2
        if(n==1||n==2)
            return n;
        //三樓4種 1 1 1 1,1 2,2 1,3
        if(n==3)
            return 4;
        //dp[i]表示i+1樓的方法數
        int[] dp=new int[n];
        dp[0]=1;
        dp[1]=2;
        dp[2]=4;
        for(int i=3;i<dp.length;i++){
            dp[i]=((dp[i-1]%num+dp[i-2]%num)%num+dp[i-3]%num)%num;
        }
        return dp[n-1];
    }

10 按摩師

在這裏插入圖片描述
跟打家劫舍是一道題

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

Medium難度

1 最長迴文子串

在這裏插入圖片描述

	public String longestPalindrome(String s) {
        int len=s.length();
        if(len<2)//長度爲0或1,返回自己
            return s;
        int maxLen=1;
        int start=0;
        //dp[i][j]表示字符串s的i到j字串是否爲迴文串
        boolean[][] dp=new boolean[len][len];
        //初始化,單個字符串肯定是迴文串
        for(int i=0;i<dp.length;i++)
            dp[i][i]=true;
        //由於是迴文串,只需要判斷一半就行
        for(int j=1;j<s.length();j++){
            for(int i=0;i<j;i++){
                //首尾不相等,肯定不是迴文串,相等判斷才繼續判斷
                if(s.charAt(i)==s.charAt(j)){
                    if(j-i<3)//表示字串長度爲0-2,首尾又相等,肯定是迴文串
                        dp[i][j]=true;
                    else
                        dp[i][j]=dp[i+1][j-1];//除去首尾繼續判斷
                } 
                if(dp[i][j]){//保持狀態
                    len=j-i+1;
                    if(len>maxLen){
                        maxLen=len;
                        start=i;
                    }
                }
            }
        }
        //從start開始,長度爲maxLen的字串就是結果
        return s.substring(start,start+maxLen);
    }

2 不同路徑

在這裏插入圖片描述

	public int uniquePaths(int m, int n) {
        //n行m列
        int[][] dp=new int[n][m];
        //到第一行哪一列都m只有一種,就是向右走幾個的問題
        for(int i=0;i<m;i++)
            dp[0][i]=1;
        //到第一列哪一行都只有一種,就是向下走幾個的問題
        for(int i=0;i<n;i++)
            dp[i][0]=1;
        //到i行j列的路徑數=到i-1行j列(上邊格子)的數量+到i行j-1列(左邊格子)的數量
        for(int i=1;i<n;i++)
            for(int j=1;j<m;j++)
                dp[i][j]=dp[i-1][j]+dp[i][j-1];
        return dp[n-1][m-1];
    }

3 不同路徑2

在這裏插入圖片描述

	public int uniquePathsWithObstacles(int[][] dp) {
        if(dp[0][0]==1)//第一個就是障礙物,不用計算了
            return 0;
        //行和列
        int m=dp.length;
        int n=dp[0].length;
        //初始化第一行
        int index=0;
        //沒遇到障礙物就初始化爲0,障礙物及其之後都初始化0
        while(index<n&&dp[0][index]!=1)
            dp[0][index++]=1;
        while(index<n){
            dp[0][index++]=0;
        }
        //初始化第一列
        index=1;
        while(index<m&&dp[index][0]!=1)
            dp[index++][0]=1;
        while(index<m){
            dp[index++][0]=0;
        }
        //動態規劃填表
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                if(dp[i][j]==1)
                    dp[i][j]=0;//當前位置是障礙物,到達路徑爲0
                else
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }

4 最小路徑和

在這裏插入圖片描述

	public int minPathSum(int[][] grid) {
        //初始化第一行
        for(int i=1;i<grid[0].length;i++){
            grid[0][i]+=grid[0][i-1];
        }
        //初始化第一列
        for(int i=1;i<grid.length;i++){
            grid[i][0]+=grid[i-1][0];
        }
        //動態規劃
        for(int i=1;i<grid.length;i++){
            for(int j=1;j<grid[0].length;j++){
                grid[i][j]+=Math.min(grid[i-1][j],grid[i][j-1]);
            }
        }
        return grid[grid.length-1][grid[0].length-1];
    }

5 解碼方法

在這裏插入圖片描述
當前位置如果不爲0,但不能和前一位數字組成1~26的數字,就等於前一位的解法數
例如1231的解法數=123的解法數

如果當前位置和前一位可以組成1~26的數字,就額外加上前兩位的解法
例如1211的解法數=121的解法數+12的解法數

	public int numDecodings(String s) {
        if(s.charAt(0)=='0')
            return 0;
        //dp[i]表示到i-1位置爲止的解密方法數
        int[] dp=new int[s.length()+1];
        dp[0]=1;
        dp[1]=1;
        for(int i=2;i<dp.length;i++){
            if(s.charAt(i-1)!='0')
                dp[i]=dp[i-1];
            if(s.charAt(i-2)=='1'||(s.charAt(i-2)=='2'&&s.charAt(i-1)<='6'))
                dp[i]+=dp[i-2];
        }
        return dp[dp.length-1];
    }

6 不同的二叉搜索樹

在這裏插入圖片描述
設dp[N]爲1-N的不同二叉樹個數,而f(i)是以i爲節點的不同二叉樹個數,那麼dp[N]=f(1)+f(2)+…+f(n),f(i)=dp(i-1)*dp(N-i),因爲左邊有i-1個節點,右邊有N-i個節點。
根據此關係寫代碼:

	public int numTrees(int n) {
        int[] dp=new int[n+1];
        dp[0]=1;
        dp[1]=1;
        for(int N=2;N<=n;N++){
            for(int i=1;i<N+1;i++){
                dp[N]+=dp[i-1]*dp[N-i];
            }
        }
        return dp[n];
    }

7 不同的二叉搜索樹2

在這裏插入圖片描述

	public List<TreeNode> generateTrees(int n) {
        if(n==0)
            return new ArrayList<TreeNode>();
        return generateTrees(1,n);
    }

    private List<TreeNode> generateTrees(int start, int end) {
        List<TreeNode> list=new ArrayList<>();
        if(start>end){
            list.add(null);
            return list;
        }
        for(int i=start;i<=end;i++){
            //以i爲根節點,i左邊爲左子樹,i右邊爲右子樹
            List<TreeNode> leftList=generateTrees(start,i-1);
            List<TreeNode> rightList=generateTrees(i+1,end);
            for(TreeNode lChild:leftList){
                for(TreeNode rChild:rightList){
                    TreeNode node = new TreeNode(i);
                    node.left=lChild;
                    node.right=rChild;
                    list.add(node);
                }
            }
        }
        return list;
    }

8 三角形最小路徑和

在這裏插入圖片描述
如果是邊界的兩個值,那麼最小值就是上面邊界的累加值加上自己,否則就是兩邊的較小值加上自己,最後把最後一行遍歷,尋找最小值即可。

	public int minimumTotal(List<List<Integer>> triangle) {
        int n=triangle.size();
        int[][] dp=new int[n][n];
        dp[0][0]=triangle.get(0).get(0);
        for(int i=1;i<n;i++){
            List<Integer> list = triangle.get(i);
            for(int j=0;j<=i;j++){
                if(j==0) {//左邊界
                    dp[i][j] = dp[i-1][j] + list.get(j);
                }else if(j==i){//右邊界
                    dp[i][j] = dp[i-1][j-1] + list.get(j);
                }else {
                    dp[i][j] = Math.min(dp[i-1][j-1],dp[i-1][j])+list.get(j);
                }
            }
        }
        int min=Integer.MAX_VALUE;
        for(int j=0;j<n;j++){
            if(dp[n-1][j]<min)
                min=dp[n-1][j];
        }
        return min;
    }

9 單詞拆分

在這裏插入圖片描述

	public boolean wordBreak(String s, List<String> wordDict) {
        //去重
        HashSet<String> set = new HashSet<>(wordDict);
        //dp[i]表示到i+1位置截至,是否可成功拆分
        boolean[] dp=new boolean[s.length()+1];
        dp[0]=true;
        for(int i=1;i<dp.length;i++){
            for(int j=0;j<i;j++){
                if(dp[j]&&set.contains(s.substring(j,i))){
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[dp.length-1];
    }

10 乘積最大子數組

在這裏插入圖片描述

	public int maxProduct(int[] nums) {
        int max=Integer.MIN_VALUE;
        int tmpMax=1;
        int tmpMin=1;
        for(int i=0;i<nums.length;i++){
            if(nums[i]<0){
                int tmp=tmpMax;
                tmpMax=tmpMin;
                tmpMin=tmp;
            }
            tmpMax=Math.max(tmpMax*nums[i],nums[i]);
            tmpMin=Math.min(tmpMin*nums[i],nums[i]);
            max=Math.max(tmpMax,max);
        }
        return max;
    }

11 打家劫舍2

在這裏插入圖片描述
相等於計算兩邊打家劫舍1,範圍是第一個到倒數第二個,或者第二個到最後一個

	public int rob(int[] nums) {       
        if(nums.length==0)
            return 0;
        if(nums.length==1)
            return nums[0];
        int max1=robHelp(Arrays.copyOfRange(nums,0,nums.length-1));
        int max2=robHelp(Arrays.copyOfRange(nums,1,nums.length));
        return Math.max(max1,max2);
    }

    public int robHelp(int[] nums) {
        int[] dp=new int[nums.length+1];
        dp[0]=0;
        dp[1]=nums[0];
        for(int i=2;i<nums.length+1;i++){
            dp[i]=Math.max(dp[i-2]+nums[i-1],dp[i-1]);
        }
        return dp[nums.length];
    }

12 最大正方形

在這裏插入圖片描述

	public int maximalSquare(char[][] matrix) {
        int maxLen=0;
        int row=matrix.length;
        int col=row>0?matrix[0].length:0;
        int[][] dp=new int[row+1][col+1];
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                if(matrix[i-1][j-1]=='1'){
                    dp[i][j]=Math.min(dp[i-1][j],Math.min(dp[i-1][j-1],dp[i][j-1]))+1;
                    maxLen=Math.max(maxLen,dp[i][j]);
                }
            }
        }
        return maxLen*maxLen;
    }

13 醜數

在這裏插入圖片描述

	public int nthUglyNumber(int n) {
        int[] dp=new int[n];
        dp[0]=1;
        int index2=0;
        int index3=0;
        int index5=0;
        for(int i=1;i<n;i++){
            int min2=dp[index2]*2;
            int min3=dp[index3]*3;
            int min5=dp[index5]*5;
            int min=Math.min(min2,Math.min(min3,min5));
            dp[i]=min;
            if(min==min2)
                index2++;
            if(min==min3)
                index3++;
            if(min==min5)
                index5++;
        }
        return dp[n-1];
    }

14 完全平方數

在這裏插入圖片描述

	public int numSquares(int n) {
        int[] dp=new int[n+1];
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<dp.length;i++){
            dp[i]=i;
            for(int j=1;i-j*j>=0;j++){
                dp[i]=Math.min(dp[i],dp[i-j*j]+1);
            }
        }
        return dp[n];
    }

15 最長上升子序列

在這裏插入圖片描述

	public int lengthOfLIS(int[] nums) {
        if(nums.length==0)
            return 0;
        int[] dp=new int[nums.length];
        Arrays.fill(dp,1);
        //保持最大值
        int res=1;
        for(int i=1;i<dp.length;i++){
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i])
                    dp[i]=Math.max(dp[i],dp[j]+1);
            }
            res=Math.max(res,dp[i]);
        }
        return res;
    }

待更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章