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

文章目录

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

系列文章目录

一、 数组类型解题方法一:二分法
二、数组类型解题方法二:双指针法
三、数组类型解题方法三:滑动窗口
四、数组类型解题方法四:模拟
五、链表篇之链表的基础操作和经典题目
六、哈希表篇之经典题目
七、字符串篇之经典题目
八、字符串篇之 KMP
九、解题方法:双指针
十、栈与队列篇之经典题目
十 一、栈与队列篇之 top-K 问题
十 二、二叉树篇之二叉树的前中后序遍历
十 三、二叉树篇之二叉树的层序遍历及相关题目
十 四、二叉树篇之二叉树的属性相关题目
十 五、 二叉树篇之二叉树的修改与构造
十 六、 二叉树篇之二叉搜索树的属性
十 七、二叉树篇之公共祖先问题
十 八、二叉树篇之二叉搜索树的修改与构造
十 九、回溯算法篇之组合问题
二 十、回溯算法篇之分割、子集、全排列问题
二十一、贪心算法篇之入门题目
二十二、贪心算法篇之进阶题目
更新中 …


前言

刷题路线来自 :代码随想录
动态规划中每一个状态一定是由上一个状态推导出来的,例如青蛙跳台阶,第 N个台阶是从N -1 或者 N-2台阶上跳上来的。
解题步骤:

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

题录

509. 斐波那契数

Leetcode 链接
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第1张图片
题解:
方式一:递归

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 链接
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第2张图片
题解:
第 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 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第3张图片
题解:

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” )。问总共有多少条不同的路径?
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第4张图片
题解:
状态(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 来表示。

Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第5张图片
题解:
同上题,改变一下初始状态,第一排和第一列遇到石头将后边也初始化为 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 ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组 是数组中的一个连续部分。
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第6张图片
题解:
状态 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 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第7张图片
题解:
例: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 链接
Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第8张图片
题解:
i 表示 为 n
j 表示以 j 为根节点
如:当 i = 3 时,j 分别为 1,2,3Leetcode 刷题笔记(二十三) ——动态规划篇之基础题目_第9张图片

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,动态规划,链表)