动态规划习题总结2

目录

一.栈和排序

1.题目

2.图解

3.代码

二.单词拆分

1.题目

2.思路图解

3.代码 

三.接雨水问题

1.题目

2.思路图解​编辑

3.代码

四.不同的二叉搜索树

1.题目

2.思路图解

3.代码

五.三角形最小路径和

1.题目

2.思路图解

3.代码

六.求二维矩阵的最大正方形

1.题目

2.思路图解

3.代码实现

(1)普通方法求解,验证每一个矩形是否满足正方形

(2)使用动态规划进行求解

七.零钱兑换

1.题目

2.思路图解

3.代码

一.栈和排序

1.题目

给你一个 1 到 n 的排列和一个栈,并按照排列顺序入栈。

你要在不打乱入栈顺序的情况下,仅利用入栈和出栈两种操作,输出字典序最大的出栈序列。

排列:指 1 到 n 每个数字出现且仅出现一次。

示例:

输入:[2,1,5,3,4]

返回值:[5,4,3,1,2]

2.图解

3.代码

    public int[] solve (int[] a) {
        // write code here
        int n = a.length;
        //保存排序好的结果
        int[] res = new int[n];
        //保存从当前元素到结尾元素中的最大值
        int[] dp = new int[n];
        Stack<Integer> stack = new Stack<>();
        dp[n-1] = a[n-1];
        int index = 0;
        //处理dp数组
        for(int i=n-2; i>=0; i--) {
            dp[i] = Math.max(dp[i+1], a[i]);
        }
        //遇到元素先入栈,如果当前元素比之后的所有元素都大,就出栈(否则就入栈)
        for(int i=0; i<n; i++) {
            stack.push(a[i]);
            while(!stack.isEmpty() && i<n-1 && stack.peek()>dp[i+1]) {
                res[index++] = stack.pop();
            }
        }
        while(!stack.isEmpty()) {
            res[index++] = stack.pop();
        }
        return res;
    }

二.单词拆分

1.题目

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。

示例:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

2.思路图解

使用动态规划进行求解,首先查看[0,i]之间的字符串是否在字典中,这里查看的方式是使用set集合,保证查找效率为O(1),然后对于[0,i]区间的又可以进行划分为[0, j], [j, i];这里的[0, j]之前的元素就可以使用dp数组来进行判断,如果[0,j]之间的字符组成的单词都在字典中,那么再判断[j, i]之间的单词是否在字典中,如果都存在,那么dp[i]就置为true;说明[0, i]之间的字符组成的字符串都在字典中。直到所有的字符都扫描完后,都存在字典中,也就是dp[s.length]=true,就可以说明能够在字典中找到该单词的拆分单词。

3.代码 

        Set<String> set = new HashSet<>(wordDict);
        //记录所有的字符串是否都存在字典单词中
        boolean[] flag = new boolean[s.length()+1];
        flag[0] =true;//代表第0个字符存在
        for(int i=1; i<=s.length(); i++) {
            for(int j=0; j<i; j++) {
                if(flag[j] && set.contains(s.substring(j, i))) {
                    flag[i] = true;//说明从0-i的所有字符都在字典中存在
                }
            }
        }
        //返回标记结果,判断是否所欲字符都在字典中
        return flag[s.length()];
    }

三.接雨水问题

1.题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例:

2.思路图解

3.代码

//方法1 
public int trap1(int[] height) {
        int n = height.length;
        int res = 0;
        for(int i=0; i<n; i++) {
            int left =0;
            for(int j=0; j<=i; j++) {
                //找当前位置左边的最大高度
                left = Math.max(left, height[j]);
            }
            int right = 0;
            for(int j=i; j<n; j++) {
                //找当前位置右边的最大高度
                right = Math.max(right, height[j]);
            }
            //求当前位置的接水量
            res += Math.min(left, right)-height[i];
        }
        return res;
    }
//方法2
public int trap2(int[] height) {
        int n = height.length;
        int[] dpL = new int[n];
        dpL[0] = height[0];
        int[] dpR = new int[n];
        dpR[n-1] = height[n-1];
        //保存每一位左边的最大高度
        for(int i=1; i<n; i++) {
            dpL[i] = Math.max(dpL[i-1], height[i]);
        }
        //保存每一位右边的最大高度
        for(int i=n-2; i>=0; i--) {
            dpR[i] = Math.max(dpR[i+1], height[i]);
        }
        int res = 0;
        for(int i=0; i<n; i++) {
            //依次寻找每一位的储水量
            res += Math.min(dpL[i], dpR[i])-height[i];
        }
        return res;
    }

 

四.不同的二叉搜索树

1.题目

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例:

2.思路图解

由于是二叉搜索树,所以以当前元素构建的时候,都是左边所有元素都比当前元素值小,右边所有元素都比当前元素值大,然后再划分为子问题,找左边的所有能够排成二叉搜索数的个数,最后统计以当前结点为根结点的所有能够组成二叉搜索树的个数;这里通过循环一次统计每一个结点构成的二叉搜索树的个数,最后求和即可。

3.代码

//递归搜索每一个结点构成的二叉搜索树的个数    
public int numTrees1(int n) {
        //说明只有一个树根或者树为空,就返回1
        if(n==0 || n==1) {
            return 1;
        }
        int count = 0;
        //统计以每一个节点为根节点构建树的个数
        for(int i=1; i<=n; i++) {
            //统计当结点左右子树组成的个数
            int leftCount = numTrees(i-1);
            int rightCount = numTrees(n-i);
            //最后求得以当前节点为根结点的二叉搜索树的个数
            count += leftCount*rightCount;
        }
        return count;
    }



//通过使用map来存储中间计算过多次的结果,提高了时间效率
    //map的键为以当前节点作为根节点,值为组成二叉搜索树的个数
    HashMap<Integer,Integer> map = new HashMap<>();
    public int numTrees2(int n) {
        if(n==0 || n==1) {
            return 1;
        }
        //说明以该节点为根的二叉搜索树的个数已经计算过了,直接返回结果即可
        if(map.containsKey(n)) {
            return map.get(n);
        }
        int count = 0;
         for(int i=1; i<=n; i++) {
             int leftCount = numTrees(i-1);
             int rightCount = numTrees(n-i);
             count += leftCount*rightCount; 

         }
        //在map中存储以i为根结点组成的二叉搜索树的个数
        map.put(n, count);
         return count;
    }

五.三角形最小路径和

1.题目

给定一个正三角形数组,自顶到底分别有 1,2,3,4,5...,n 个元素,找出自顶向下的最小路径和。

每一步只能移动到下一行的相邻节点上,相邻节点指下行种下标与之相同或下标加一的两个节点。

例如当输入[[2],[3,4],[6,5,7],[4,1,8,3]]时,对应的输出为11.

2.思路图解

首先寻找关系,根据关系写出动态规划的方程。根据题目,我们可以从最后一行开始构建dp数组,当为最后一行时,就是dp数组指定的值,之后每一个位置的dp[i][j]=min(dp[i+1][j+1])+triangle[i][j];

根据题目可得,当前位置的值就是当前位置下一行对应的位置和右边的最小值,最终结果为dp[0][0]就为最小值。

3.代码

 public int minTrace (int[][] triangle) {
        if(triangle.length==0) {
            return 0;
        }
        int len = triangle[triangle.length-1].length;
        int[][] dp = new int[len][len];
        //从下到上依次动态规划进行向上查找
        for(int i=len-1; i>=0; i--) {
            for(int j=0; j<=i; j++) {
                if(i==len-1) {
                    dp[i][j] = triangle[i][j];
                }else  {
                    //dp[i][j]就为当前位置的结果当前位置的下和右dp的最小值
                    dp[i][j] = Math.min(dp[i+1][j+1], dp[i+1][j])+triangle[i][j];
                }
            }
        }
        return dp[0][0];
    }

六.求二维矩阵的最大正方形

1.题目

在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

示例:

 

2.思路图解

解法1的思路:

解法2的思路:

 

 

 

3.代码实现

(1)普通方法求解,验证每一个矩形是否满足正方形

 public int maximalSquare(char[][] matrix) {
        int rs = matrix.length;
        int cs = matrix[0].length;
        int maxSide = 0;
        for(int i=0; i<rs; i++) {
            for(int j=0; j<cs; j++) {
                if(matrix[i][j]=='1'){
                    maxSide = Math.max(maxSide, 1);
                    //如果左上角的1存在就找开始找最大矩形
                    //首先从最大矩形开始匹配
                    int currentSide = Math.min(rs-i, cs-j);
                    for(int k=1; k<currentSide; k++) {
                    //首先判断对角线元素是否满足
                    if(matrix[i+k][j+k]=='0') {
                        //说明不符合直接返回
                        break;
                        }
                        boolean flag = true;
                        //然后依次匹配当前行和当前列的元素
                        for(int x=0; x<k; x++) {
                            if(matrix[i+k][j+x]=='0' || matrix[i+x][j+k]=='0') {
                                //说明当前列有不符合,标志位修改,然后退出循环
                                flag = false;
                                break;
                            }
                        }
                        //如果flag=true;说明当前边长加1的矩形符合要正方形
                        //,那么修改最大边长,否则直接退出
                        if(flag) {
                            maxSide = Math.max(maxSide, k+1);
                        }else {
                            break;
                        }

                    }
                }
            }
        }
        return maxSide*maxSide;
    }

(2)使用动态规划进行求解

    public int maximalSquare(char[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        //保存最大边长
        int maxSide = 0;
        int[][] dp = new int[rows][cols];
        for(int i=0; i<rows; i++) {
            for(int j=0; j<cols; j++) {
                if(matrix[i][j]=='1') {
                    //边界问题
                    if(i==0 || j==0) {
                        dp[i][j] = 1;
                    } else {
                        //根据dp确定正方形大小
                        //dp数组中存储的是每一个位置能够成为正方形的最大值边长
                        //这里的正方形的右下角由左上,左,上三个位置组成,所以取最小值加1
                        dp[i][j] = Math.min(dp[i-1][j-1], Math.min(dp[i][j-1],dp[i-1][j]))+1;
                    }
                    //确定边界大小
                maxSide = Math.max(dp[i][j], maxSide);
                }
            }
        }
        return maxSide*maxSide;
    }

七.零钱兑换

1.题目

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/coin-change
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:

输入:coins = [1, 2, 5], amount = 6

输出:2
 
解释:6 = 5 + 1

2.思路图解

 

3.代码

public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];
        Arrays.fill(dp, amount+1);
        dp[0] = 0;
        //i 表示当前的钱数,计算从0到amount每个钱需要的硬币数
        for(int i=0; i<=amount; i++) {
            for(int j=0; j<coins.length; j++) {
                //判断当前的硬币金额是否可以构成当前的一部分钱数
                if(i>=coins[j]) {
                    dp[i] = Math.min(dp[i], dp[i-coins[j]]+1);
                }
            }
        }
        //根据最后的结果是否为是否可以构成零钱兑换
        return dp[amount]==amount+1?-1:dp[amount];
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值