一、 数组类型解题方法一:二分法
二、数组类型解题方法二:双指针法
三、数组类型解题方法三:滑动窗口
四、数组类型解题方法四:模拟
五、链表篇之链表的基础操作和经典题目
六、哈希表篇之经典题目
七、字符串篇之经典题目
八、字符串篇之 KMP
九、解题方法:双指针
十、栈与队列篇之经典题目
十 一、栈与队列篇之 top-K 问题
十 二、二叉树篇之二叉树的前中后序遍历
十 三、二叉树篇之二叉树的层序遍历及相关题目
十 四、二叉树篇之二叉树的属性相关题目
十 五、 二叉树篇之二叉树的修改与构造
十 六、 二叉树篇之二叉搜索树的属性
十 七、二叉树篇之公共祖先问题
十 八、二叉树篇之二叉搜索树的修改与构造
十 九、回溯算法篇之组合问题
二 十、回溯算法篇之分割、子集、全排列问题
二十一、贪心算法篇之入门题目
二十二、贪心算法篇之进阶题目
更新中 … …
刷题路线来自 :代码随想录
动态规划中每一个状态一定是由上一个状态推导出来的,例如青蛙跳台阶,第 N个台阶是从N -1 或者 N-2台阶上跳上来的。
解题步骤:
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;
}
}
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];
}
}
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]);
}
}
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];
}
}
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];
}
}
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;
}
}
Leetcode 链接
给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。
题解:
例:5 可以分解为 1 + 4 、2 + 3、3 + 2、4 + 1
dp[1] * 4、dp[2] * 3、dp[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];
}
}
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];
}
}