复习 509 斐波那契数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int fib (int n) { if (n < 1 ) return n; int [] dp = new int [n + 1 ]; dp[0 ] = 0 ; dp[1 ] = 1 ; for (int i = 2 ; i < n + 1 ; i++){ dp[i] = dp[i - 1 ] + dp[i - 2 ]; } return dp[n]; } }
70 爬楼梯 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Solution { public int climbStairs (int n) { if (n <= 1 ) return n; int [] dp = new int [n + 1 ]; 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]; } }
746 使用最小花费爬楼梯 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int minCostClimbingStairs (int [] cost) { int n = cost.length; if (n <= 1 ) return 0 ; int [] dp = new int [n + 1 ]; dp[0 ] = 0 ; dp[1 ] = 0 ; for (int i = 2 ; i < n + 1 ; i++){ dp[i] = Math.min(dp[i-1 ] + cost[i - 1 ], dp[i - 2 ] + cost[i - 2 ]); } return dp[n]; } }
62 不同路径 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public int uniquePaths (int m, int n) { int [][] dp = new int [m][n]; for (int i = 0 ; i < m; i++){ dp[i][0 ] = 1 ; } for (int i = 0 ; i < n; i++){ dp[0 ][i] = 1 ; } for (int i = 1 ; i < m; i++){ for (int j = 1 ; j < n; j++){ dp[i][j] = dp[i - 1 ][j] + dp[i][j - 1 ]; } } return dp[m - 1 ][n - 1 ]; } }
63 不同路径II 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public int uniquePathsWithObstacles (int [][] obstacleGrid) { int m = obstacleGrid.length; int n = obstacleGrid[0 ].length; int [][] dp = new int [m][n]; for (int i = 0 ; i < m && obstacleGrid[i][0 ] != 1 ; i++){ dp[i][0 ] = 1 ; } for (int i = 0 ; i < n && obstacleGrid[0 ][i] != 1 ; i++){ dp[0 ][i] = 1 ; } for (int i = 1 ; i < m; i++){ for (int j = 1 ; j < n; j++){ if (obstacleGrid[i][j] != 1 ){ dp[i][j] = dp[i - 1 ][j] + dp[i][j - 1 ]; } } } return dp[m - 1 ][n - 1 ]; } }
343 拆分整数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int integerBreak (int n) { int [] dp = new int [n + 1 ]; dp[2 ] = 1 ; for (int i = 3 ; i < n + 1 ; i++){ for (int j = 1 ; j < i; j++){ dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j])); } } return dp[n]; } }
96 不同的二叉搜索树 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int numTrees (int n) { int [] dp = new int [n + 1 ]; dp[0 ] = 1 ; dp[1 ] = 1 ; for (int i = 2 ; i < n + 1 ; i++){ for (int j = 0 ; j < i; j++){ dp[i] += dp[j] * dp[i - j - 1 ]; } } return dp[n]; } }
01背包理论基础 二维 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int [] weights;int [] values;int size;int len = weights.length;int [][] dp = new int [len + 1 ][size + 1 ];for (int i = 1 ; i < len + 1 ; i++){ for (int j = 1 ; j < size + 1 ; j++){ if (j < weights[i - 1 ]){ dp[i][j] = dp[i - 1 ][j]; } else { dp[i][j] = Math.max(dp[i - 1 ][j], dp[i - 1 ][j - weights[i - 1 ]] + values[i - 1 ]); } } } return dp[len][size];
动态规划 01背包理论基础(滚动数组)一维 1 2 3 4 5 6 7 8 9 10 11 12 13 int [] weights;int [] values;int size;int [] dp = new int [size + 1 ];for (int i = 0 ; i < weights.length; i++){ for (int j = size; j >= weights[i]; j--){ dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]); } } return dp[size];
416 分割等和子集 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution { public boolean canPartition (int [] nums) { if (nums.length == 0 || nums == null ) return false ; Arrays.sort(nums); int sum = 0 ; for (int i : nums){ sum += i; } if (sum % 2 != 0 ) return false ; int bagSize = sum / 2 ; int [] dp = new int [bagSize + 1 ]; for (int i = 0 ; i < nums.length; i++){ for (int j = bagSize; j >= nums[i]; j--){ dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]); } if (dp[bagSize] == bagSize) return true ; } return dp[bagSize] == bagSize; } }
1049 最后一块石头的重量II 动态规划五步曲:
确定dp数组以及下标的含义 dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。
递推公式 dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
初始化
遍历顺序
模拟过程 把整个石头分成两堆就是,dp[size] 和 sum-dp[size]; 因为sum / 2 是向下取整,所以sum- dp[size] 一定大于 dp[size]; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int lastStoneWeightII (int [] stones) { if (stones.length == 0 || stones == null ) return 0 ; int sum = 0 ; for (int i: stones){ sum += i; } int size = sum / 2 ; int [] dp = new int [size + 1 ]; for (int i = 0 ; i < stones.length; i++){ for (int j = size; j >= stones[i]; j--){ dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]); } } return (sum - dp[size]) - dp[size]; } }
494 目标和 此题转化为01背包问题 假设加法总和为x, 减法总和是sum - x 所以要求 x - (sum - x)= target x = (target + sum) / 2
确定dp数组以及下标的含义 dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
确定递推公式 dp[j],j 为5,
已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包 累加dp[j - nums[i]]
组合问题常用递推公式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public int findTargetSumWays (int [] nums, int target) { if (nums == null || nums.length == 0 ) return 0 ; int sum = 0 ; for (int i : nums){ sum += i; } if (target < 0 && -target > sum) return 0 ; if ((target + sum) % 2 != 0 ) return 0 ; int bagSize = (target + sum) / 2 ; if (bagSize < 0 ) bagSize = -bagSize; int [] dp = new int [bagSize + 1 ]; dp[0 ] = 1 ; for (int i = 0 ; i < nums.length; i++){ for (int j = bagSize; j >= nums[i]; j--){ dp[j] += dp[j - nums[i]]; } } return dp[bagSize]; } }
474 一和零
确定dp数组(dp table)以及下标的含义 dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
确定递推关系 dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。
dp[i][j] 就可以是 dp[i - zeroNum][j - oneNum] + 1。 dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public int findMaxForm (String[] strs, int m, int n) { if (strs == null || strs.length == 0 ) return 0 ; int [][] dp = new int [m + 1 ][n + 1 ]; int zeroNum, oneNum; for (String str : strs){ oneNum = 0 ; zeroNum = 0 ; for (char ch : str.toCharArray()){ if (ch == '0' ) zeroNum++; else oneNum++; } for (int i = m; i >= zeroNum; i--){ for (int j = n; j >= oneNum; j--){ dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1 ); } } } return dp[m][n]; } }
每日一题: 451 Sort Characters By Frequency 利用priority queue, 把value排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public String frequencySort (String s) { Map<Character, Integer> map = new TreeMap <>(); char [] chars = s.toCharArray(); for (char i: chars){ map.put(i, map.getOrDefault(i, 0 ) + 1 ); } PriorityQueue<Map.Entry<Character, Integer>> pq = new PriorityQueue <>( (a, b) -> b.getValue() - a.getValue() ); pq.addAll(map.entrySet()); StringBuilder sb = new StringBuilder (); while (!pq.isEmpty()){ Map.Entry<Character, Integer> entry = pq.poll(); sb.append(String.valueOf(entry.getKey()).repeat(entry.getValue())); } return new String (sb); } }