前言
所有题目均来自力扣题库中的hot 100,之所以要记录在这里,只是方便后续复习
53.最大子数组和
题目:
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
解题思路:
【动态规划】每个位置的最大和分两种情况考虑:截止到前一个位置的最大和+当前值,与只是当前值(不加前面位置的最大和,这种情况一般是前一个位置的最大和小于0),这两种情况我们选择较大值即当前位置的最大和,所以,每个位置的最大和只与前一个位置的最大和有关。因此可以考虑动态规划,即初始化一个变量用来存储截止到当前位置的最大和,遍历整个数组进行递推,每次都要更新该变量用于下一次循环,同时在递推的过程中要再用一个变量记录整个过程中所出现的最大和,每次循环时都要将该值与当前最大和作比较,取较大者更新该值,当递推完成,该值即结果值
代码(python):
class Solution(object):
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
# 定义变量 用来存储 截止到当前位置的最大和
pre = 0
# 定义结果变量 用来存储整个遍历过程中 所出现过的最大和,即最大的 pre
max_sum = nums[0]
# 从头开始递推,一直到数组结尾
for num in nums:
# 每个位置的最大和考虑两种情况:之前的最大和加上当前值 与 不要之前的最大和只有当前值(之前的最大和为负数的情况);两者之间的较大值即截止到当前位置的最大和
# 更新当前位置的最大和
pre = max(pre + num, num)
# 对比结果变量 和 当前最大和,将更大的值赋给结果变量
max_sum = max(max_sum, pre)
return max_sum
代码(java):
class Solution {
public int maxSubArray(int[] nums) {
int result = nums[0];
int currentSum = 0;
for(int num: nums){
currentSum = currentSum + num;
result = currentSum > result ? currentSum : result;
currentSum = currentSum > 0 ? currentSum : 0;
}
return result;
}
}
知识点:
- 无
原题链接:最大子数组和
70.爬楼梯
题目:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
- 1 阶 + 1 阶
- 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。 - 1 阶 + 1 阶 + 1 阶
- 1 阶 + 2 阶
- 2 阶 + 1 阶
解题思路:
【动态规划】假设有n层楼梯,我们这一次有两种选择 :上1层 和 上2层;当上1层时,剩下的是n-1层也就转化为n-1层的方法数,当上2层时,剩下的是n-2层也就转化为n-2层的方法数,所以n层楼梯的方法数为 n-1层的方法数 + n-2层的方法数,只与n-1 和n-2 有关,可以考虑动态规划。我们用初始化1层楼梯的方法数为1,2层楼梯的方法数为2,如果n为1或2直接返回即可,如果n>=3,我们可以从3开始递推,得到3、 4、 5直到n的方法数,每次循环都更新n-1和n-2的值用于下一次递推
代码(python):
class Solution:
def climbStairs(self, n: int) -> int:
n_2 = 0
n_1 = 0
n_0 = 1
for i in range(0, n):
n_2 = n_1
n_1 = n_0
n_0 = n_2 + n_1
return n_0
代码(java):
class Solution {
public int climbStairs(int n) {
// 定义n = 1的情况,即1层楼梯,只有一种方法
int n_2 = 1;
// 定义n = 2的情况,即2层楼梯,有两种方法
int n_1 = 2;
// 当 n = 1,直接返回
if (n == 1){
return n_2;
}
// 当 n = 2,直接返回
if(n == 2){
return n_1;
}
// 当 n > 3 定义结果值
int result = 0;
// 进行递推
for(int i = 3; i <= n; i++){
// 两种情况,这一次只上1层 ; 这一次上两层; 相加 即当前层数的方法数
result = n_2 + n_1;
// 更新n-1层 和 n-2层的方法数,用于下一次循环
n_2 = n_1;
n_1 = result;
}
return result;
}
}
知识点:
- 无
原题链接:爬楼梯
62.不同路径
题目:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
输入:m = 3, n = 7
输出:28
示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
- 向右 -> 向下 -> 向下
- 向下 -> 向下 -> 向右
- 向下 -> 向右 -> 向下
示例 3:
输入:m = 7, n = 3
输出:28
示例 4:
输入:m = 3, n = 3
输出:6
解题思路:
【动态规划】对于网格中的每个位置,都有俩个方式到达:从上面网格往下走一个 和 从左面网格往右走一个,那么该网格到达的路径数就等于 到达上面网格的路径数 + 到达左面网格的路径数,所以想到动态规划;那动态规划的起始值是什么呢?就是第一行和第一列的路径数,由于只能下走和右走,所以第一行和第一列的路径数均为1;得知了起始值和递推关系,递推每个位置的路径数即可
代码(python):
class Solution(object):
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
# 定义 结果列表,与网格一一对应,每个位置代表该位置的路径数
result = list()
for i in range(m):
item = [0] * n
result.append(item)
# 初始化第一行 和 第一列的位置的 路径数 ,均为1
for i in range(m):
result[i][0] = 1
for j in range(n):
result[0][j] = 1
# 从第二行 第二列开始 递推 每个位置的路径数:上面位置的路径数 + 左面位置的路径数
for i in range(1, m):
for j in range(1, n):
result[i][j] = result[i-1][j] + result[i][j-1]
# 返回右下角 的结果值即可
return result[m-1][n-1]
代码(java):
class Solution {
public int uniquePaths(int m, int n) {
int[][] result = new int[m][n];
for (int i = 0; i < m; i++){
result[i][0] = 1;
}
for (int j = 0; j < n; j++){
result[0][j] = 1;
}
for(int i = 1; i < m; i++){
for (int j = 1; j < n; j++){
result[i][j] = result[i - 1][j] + result[i][j - 1];
}
}
return result[m - 1][n - 1];
}
}
知识点:
- python中可以用[val] * n 定义一个包含有n个val的列表
原题链接:不同路径
64.最小路径和
题目:
给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12
解题思路:
【动态规划】对于网格中的每个位置,都有俩个方式到达:从上面网格往下走一个 和 从左面网格往右走一个,那么该网格到达的最小值就等于 到达上面网格的最小值和到达左面网格的最小值中的较小值 + 当前网格中的值,所以想到动态规划;那动态规划的起始值是什么呢?就是第一行和第一列的路径数,由于只能下走和右走,所以每个位置网格的最小值 =左面网格的最小值或上面网格的最小值 + 当前网格值;得知了起始值和递推关系,递推每个位置的最小值即可
代码(python):
class Solution(object):
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
for i in range(0, len(grid)):
for j in range(0, len(grid[0])):
if i == 0 and j == 0:
continue
if i == 0:
grid[i][j] = grid[i][j] + grid[i][j - 1]
continue
if j == 0:
grid[i][j] = grid[i][j] + grid[i - 1][j]
continue
grid[i][j] = grid[i][j] + min(grid[i][j - 1], grid[i - 1][j])
return grid[len(grid) - 1][len(grid[0]) - 1]
代码(java):
class Solution {
public int minPathSum(int[][] grid) {
// 定义结果网格 和 参数网格一一对应,表示到达该位置的最小值
int[][] result = new int[grid.length][grid[0].length];
// 初始化 结果网格第一列,每个位置(除了第一个网格)的值都是 上面网格的值 + 当前位置参数网格中的值
for (int i = 0; i < result.length; i ++){
if(i == 0){
result[i][0] = grid[i][0];
}else{
result[i][0] = result[i - 1][0] + grid[i][0];
}
}
// 初始化 结果网格第一行,每个位置的值都是 左面网格的值 + 当前位置参数网格中的值
for(int j = 1; j < result[0].length; j ++){
result[0][j] = result[0][j - 1] + grid[0][j];
}
// 从第二行第二列开始 递推 每个结果网格的值
for(int i = 1; i < result.length; i ++){
for (int j = 1; j < result[0].length; j++){
// 每个位置的最小值 = 上面网格的最小值和左面网格最小值中的较小值 + 当前位置参数网格中的值
result[i][j] = Math.min(result[i - 1][j], result[i][j - 1]) + grid[i][j];
}
}
return result[result.length - 1][result[0].length - 1];
}
}
知识点:
- 无
原题链接:最小路径和
96.不同的二叉搜索树
题目:
给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3
输出:5
示例 2:
输入:n = 1
输出:1
解题思路:
【动态规划】对于n个节点,每个节点都可能做根节点,假设 i 做了根节点,根据二叉搜索的性质左子数都要小于根节点,右子树都要大于根节点,那么左子树节点就有i - 1个,右子树节点就有n - i个,那这种情况下组成的二叉搜索数个数是多少呢?左子数的个数 * 右子树的个数,即笛卡尔乘积。那对于n个节点右多数中这种情况呢?即 i 的个数 从 1 到n,所以n个节点个数就是这n个情况之和;综上所述每个节点的个数都对之前节点的个数由明确的依赖,因此可以考虑动态规划递推出n的个数,那动态规划的起始值是什么呢?当n为0和1时,个数均是1
代码(python):
class Solution(object):
def numTrees(self, n):
"""
:type n: int
:rtype: int
"""
results = [0] * (n + 1)
results[0] = 1
results[1] = 1
for i in range(2, n + 1):
for j in range(1, i + 1):
results[i] = results[i] + results[j - 1] * results[i - j]
return results[n]
代码(java):
class Solution {
public int numTrees(int n) {
// 定义结果集合,长度是n + 1,每个位置代表着 i 个节点二叉搜索数的个数
int[] result = new int[n + 1];
// 定义初始值
result[0] = 1;
result[1] = 1;
// 从 i 为2 开始递推 结果集合 注意到n结束
for(int i = 2; i <= n; i ++){
// 每个位置的值等于 j 从1到i,以j为根节点,左面节点个数 j -1 和 右面节点个数 i - j 的笛卡尔乘积 的和
for(int j = 1; j <= i; j ++){
result[i] += result[j - 1] * result[i - j];
}
}
return result[n];
}
}
知识点:
- 无
原题链接:不同的二叉搜索树