失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目

Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目

时间:2019-05-23 13:34:55

相关推荐

Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目

文章目录

系列文章目录前言题录509. 斐波那契数70. 爬楼梯746. 使用最小花费爬楼梯62. 不同路径63. 不同路径 II53. 最大子数组和343. 整数拆分96.不同的二叉搜索树

系列文章目录

一、 数组类型解题方法一:二分法

二、数组类型解题方法二:双指针法

三、数组类型解题方法三:滑动窗口

四、数组类型解题方法四:模拟

五、链表篇之链表的基础操作和经典题目

六、哈希表篇之经典题目

七、字符串篇之经典题目

八、字符串篇之 KMP

九、解题方法:双指针

十、栈与队列篇之经典题目

十 一、栈与队列篇之 top-K 问题

十 二、二叉树篇之二叉树的前中后序遍历

十 三、二叉树篇之二叉树的层序遍历及相关题目

十 四、二叉树篇之二叉树的属性相关题目

十 五、 二叉树篇之二叉树的修改与构造

十 六、 二叉树篇之二叉搜索树的属性

十 七、二叉树篇之公共祖先问题

十 八、二叉树篇之二叉搜索树的修改与构造

十 九、回溯算法篇之组合问题

二 十、回溯算法篇之分割、子集、全排列问题

二十一、贪心算法篇之入门题目

二十二、贪心算法篇之进阶题目

更新中 ……


前言

刷题路线来自 :代码随想录

动态规划中每一个状态一定是由上一个状态推导出来的,例如青蛙跳台阶,第 N个台阶是从N -1 或者 N-2台阶上跳上来的。

解题步骤:

状态(dp数组以及下标的含义)递推公式 和 循环结构初始化返回值

题录

509. 斐波那契数

Leetcode 链接

题解:

方式一:递归

class Solution {public int fib(int n) {if (n == 0 || n == 1) return n;return fib(n - 1) + fib(n - 2);}}

方式二:动态规划

class Solution {public int fib(int n) {if (n == 0) return 0;if (n == 1) return 1;int[] dp = new int[n + 1];dp[0] = 0;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}}

空间优化:

class Solution {public int fib(int n) {if (n == 0) return 0;if (n == 1) return 1;int a = 0;int b = 1;int res = 0;for (int i = 2; i <= n; i++) {res = a + b;a = b;b = res;}return res;}}

70. 爬楼梯

Leetcode 链接

题解:

第 N 层楼梯是由第 N - 1 或 N - 2 层跳上来的

dp 数组

class Solution {public int climbStairs(int n) {if (n == 1) return 1;int[] dp = new int[n + 1];dp[0] = 1;dp[1] = 1;for (int i = 2; i <= n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n];}}

746. 使用最小花费爬楼梯

Leetcode 链接

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。

题解:

class Solution {public int minCostClimbingStairs(int[] cost) {int len = cost.length;int[] dp = new int[len];dp[0] = cost[0];dp[1] = cost[1];for (int i = 2; i < len; i++) {dp[i] = Math.min(dp[i - 1], dp[i - 2]) +cost[i];}return Math.min(dp[len - 1], dp[len - 2]);}}

62. 不同路径

Leetcode 链接

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。问总共有多少条不同的路径?

题解:

状态(i, j):从(0,0) 到(i, j)的路径个数

状态方程:F(i,j) = F(i-1,j) + F(i,j-1)

初始状态:F(0,0) = F(0,j) = F(i,0) = 1

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];}}

优化:

状态(i,j):从(0,0) 到(i,j)的路径个数

状态方程:F(i,j) = F(i-1,j) + F(i,j-1)

初始状态:F(0,j) = F(i,0) = 0, F(0,1) = 1或者F(1,0) = 1

返回结果:F(row,col)

class Solution {public int uniquePaths(int m, int n) {int[][] dp = new int[m + 1][n + 1];dp[0][1] = 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][n];}}

63. 不同路径 II

Leetcode 链接

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?网格中的障碍物和空位置分别用 1 和 0 来表示。

题解:

同上题,改变一下初始状态,第一排和第一列遇到石头将后边也初始化为 0,中间区域只让有石头的地方为 0

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int row = obstacleGrid.length;int col = obstacleGrid[0].length;int[][] dp = new int[row][col];for (int i = 0; i < row && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;for (int j = 0; j < col && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;for (int i = 1; i < row; i++) {for (int j = 1; j < col; j++) {if (obstacleGrid[i][j] == 0) {dp[i][j] = dp[i - 1][j] + dp[i][j -1];} else {dp[i][j] = 0;}}}return dp[row - 1][col - 1];}}

优化:

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {int row = obstacleGrid.length;int col = obstacleGrid[0].length;int[][] dp = new int[row + 1][col + 1];dp[0][1] = 1;for (int i = 1; i <= row; i++) {for (int j = 1; j <= col; j++) {if (obstacleGrid[i - 1][j - 1] == 1) {dp[i][j] = 0;}else {dp[i][j] = dp[i][j - 1] + dp[i -1][j];}}}return dp[row][col];}}

53. 最大子数组和

Leetcode 链接

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。

题解:

状态 dp[i]:算上 i 位置的最长连续子数组和

状态方程:dp[i] = Math.max(dp[i - 1] + nums[i], nums[i])

返回值:遍历过程中的 maxSum

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

343. 整数拆分

Leetcode 链接

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。

题解:

例:5 可以分解为1 + 4 、2 + 33 + 2、4 + 1

dp[1] * 4、dp[2] * 3dp[3] * 2、dp[4] * 1、 但是这里并没有最大值

dp[2] = 1 * 1 = 1、dp[3] = 1 * 2 = 2最大的 dp[3] * 2 = 4

最大的是6: 5 = 2 + 3、2 * 3 = 6

而对于较大的数是更是满足递推公式的,所以递推公式中有:Math.max(dp[j] * (i - j), j * (i - j))

状态 dp[i]:整数 i 的拆分最大乘积

状态方程:Math.max(dp[i], Math.max(dp[j] * (i - j), j * (i - j)));

也可以这么理解,j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。

初始化:因为 k >= 2,dp[2] = 0

返回:dp[n]

class Solution {public int integerBreak(int n) {int[] dp = new int[n + 1];dp[2] = 1;for (int i = 3; i <= n; i++) {for (int j = 1; j <= i - 1; j++) {dp[i] = Math.max(dp[i], Math.max(dp[j] * (i - j), j * (i - j)));}}return dp[n];}}

96.不同的二叉搜索树

Leetcode 链接

题解:

i 表示 为 n

j 表示以 j 为根节点

如:当 i = 3 时,j 分别为 1,2,3

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; i++) {for (int j = 1; j <= i; j++) {dp[i] += dp[j- 1] * dp[i - j];}}return dp[n];}}

如果觉得《Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。