当前位置: 首页 > news >正文

力扣hot100 | 动态规划1 | 70. 爬楼梯、118. 杨辉三角、198. 打家劫舍、279. 完全平方数、322. 零钱兑换

70. 爬楼梯

力扣题目链接
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶

示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶

一、递归 + 记录返回值 = 记忆化搜索

参见灵茶山艾府

class Solution:def climbStairs(self, n: int) -> int:@cache  # 缓存装饰器,避免重复计算 dfs 的结果—— 这行不写会超时def dfs(i: int) -> int:if i <= 1:return 1return dfs(i - 1) + dfs(i - 2)return dfs(n)

加速前

  • 时间复杂度 O(2n2^n2n)。搜索树可以近似为一棵二叉树,树高为 O(n),所以节点个数为 O(2n2^n2n),遍历搜索树需要 O(2n2^n2n)的时间。
  • 空间复杂度 O(n)。递归需要 O(n) 的栈空间。

加速后

  • 时间复杂度 O(n)。由于每个状态只会计算一次,动态规划的时间复杂度 = 状态个数 × 单个状态的计算时间。本题状态个数等于 O(n),单个状态的计算时间为 O(1),所以动态规划的时间复杂度为 O(n)。
  • 空间复杂度 O(n)。有多少个状态,memo 数组的大小就是多少。

二、递推

  • dp[i]定义:从0爬到i层(爬了i层)的方案数。所以答案就是dp[n]
  • 递推公式:dp[i] = dp[i - 1] + dp[i - 2]
  • 初始化:dp[1] = 1,dp[2] = 2(若要dp[0] = dp[1] = 1,则遍历可从i=2开始,但相当于强加了dp[0]的意义)【但选择有意义的初始化方法的话,一定不能忘记在开头特判n==1,不然初始化dp[2]会报错】
  • 遍历【方向+边界】:从左到右、从dp[3]开始、到dp[n]结束。
class Solution:def climbStairs(self, n: int) -> int:if n == 1:  # 别忘这个!!return ndp = [0] * (n+1)dp[1] = 1dp[2] = 2  # 不然这里会报错for i in range(3, n+1): # 这里却不会,因为range()中就算输入不合法的值,也只是返回 空的 range 对象dp[i] = dp[i-1] + dp[i-2]return dp[n]
  • 时间复杂度 O(n)
  • 空间复杂度 O(n)

三、空间优化

其实只需维护三个整数即可:

class Solution:def climbStairs(self, n: int) -> int:if n == 1: return 1dp1 = 1dp2 = 2  for i in range(3, n+1): dp3 = dp1 + dp2dp1, dp2 = dp2, dp3return dp2
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)

118. 杨辉三角

力扣题目链接
给定一个非负整数 numRows,生成「杨辉三角」的前numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
在这里插入图片描述

示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]

示例 2:
输入: numRows = 1
输出: [[1]]

class Solution:def generate(self, numRows: int) -> List[List[int]]:res = [[1] * (i + 1) for i in range(numRows)]for i in range(2, numRows): # 前两行都是1不用改for j in range(1, i): # 第i行有i+1列(末尾是第i列),但不用改第i列res[i][j] = res[i - 1][j - 1] + res[i - 1][j]return res
  • 时间复杂度 O(numRows2)O(numRows^2)O(numRows2)
  • 空间复杂度 O(1)

198. 打家劫舍

力扣题目链接
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。

一、递归 + 记忆缓存

from functools import cache # python3.9开始
# from functools import lru_cache as cache # 3.9以下的版本
class Solution:def rob(self, nums: List[int]) -> int:@cachedef dfs(i): # dfs(i) 表示从 nums[0] 到 nums[i] 最多能偷多少if i < 0:return 0# 不偷i     偷i    return max(dfs(i-1), dfs(i-2) + nums[i])return dfs(len(nums) - 1)
  • 时间复杂度 O(n)
  • 空间复杂度 O(n)

二、递推

参考自灵茶山艾府

dp数组
class Solution:def rob(self, nums: List[int]) -> int:f = [0] * (len(nums) + 2)for i, x in enumerate(nums):f[i + 2] = max(f[i + 1], f[i] + x)return f[-1]
  • 时间复杂度 O(n)
  • 空间复杂度 O(n)
空间优化
class Solution:def rob(self, nums: List[int]) -> int:f0 = f1 = 0for x in nums:f0, f1 = f1, max(f1, f0 + x)return f1
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)

279. 完全平方数【完全背包】

力扣题目链接
给你一个整数 n ,返回 和为 n完全平方数的最少数量

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4

示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9

参考自灵茶山艾府。

递归

  • 【注意几点】:
    • 要把记忆化搜索的 memo 数组声明为全局变量,这样可以在多个测试数据之间共享,从而减少计算量。Python 可以把 dfs 写在类外面若跟之前一样写在类中,力扣会因超出内存限制而失败!
    • 为什么本题的递归边界是 i=0?之前的那些 DP 题的递归边界都是 i<0。——本题最小的完全平方数是 121^212 ,递归到 i=0 就说明所有完全平方数都考虑完了。
      (其他题目最小的数一般是下标为 0 的数,递归到 i < 0 才说明所有的数都考虑完了。)
import math
@cache
def dfs(i, c): #表示第1~i(包括i)个完全平方数中任选、恰好和为n的,最少数量if i == 0:return 0 if c == 0 else float('inf') # 求最小值if c < i ** 2: # 【注意】第i(1-based)个平方数是i^2return dfs(i-1, c)return min(dfs(i-1, c), dfs(i, c-i**2) + 1)class Solution:def numSquares(self, n: int) -> int:# 递归入口:i^2 <= n才不会inf,所以i从不大于 根号n 开始取就可以# math.isqrt()返回不超过 math.sqrt(n) 的最大整数(向下取整)return dfs(math.isqrt(n), n)
  • 时间复杂度 O(n⋅nn\cdot \sqrt nnn),由于每个状态只会计算一次,动态规划的时间复杂度=状态个数×单个状态的计算时间动态规划的时间复杂度 = 状态个数 × 单个状态的计算时间动态规划的时间复杂度=状态个数×单个状态的计算时间。本题状态个数等于O(n⋅nn\cdot \sqrt nnn),单个状态的计算时间为 O(1)。
  • 空间复杂度 O(n⋅nn\cdot \sqrt nnn)。保存多少状态,就需要多少空间。

322. 零钱兑换【完全背包】

力扣题目链接
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回-1

你可以认为每种硬币的数量是无限的。

示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0

递归

from functools import cache
class Solution:def coinChange(self, coins: List[int], amount: int) -> int:@cachedef dfs(i, c): # 代表[0:i](包括i)硬币中任选,刚好组成c的,最少硬币个数if i < 0:return 0 if c == 0 else float('inf') # 容量为0时只需0个硬币,否则返回不干扰min()取值的infif c < coins[i]: # 容量不够不能选return dfs(i-1, c)# 不选           本轮选一个i(下轮还能选i)return min(dfs(i-1, c), dfs(i, c-coins[i]) + 1) # 求硬币数,所以+1res = dfs(len(coins) - 1, amount)return res if res != float('inf') else -1 # 如例2,最后min(inf, inf+1)不合法
  • 时间复杂度 O(n · amount),其中 n 为 coins 的长度。
  • 空间复杂度 O(n · amount)
http://www.dtcms.com/a/392885.html

相关文章:

  • 每天五分钟深度学习:softmax回归的交叉熵损失的前向传播
  • leetcode算法刷题的第四十天
  • 算法基础篇(3)高精度
  • Java Log
  • 最常见的MCP服务
  • 如何安装tomcat服务器以及如何解决服务器的乱码问题
  • 软考中级习题与解答——第九章_信息安全(1)
  • 小迪安全v2023学习笔记(八十五讲)—— APP攻防反证书反代理反模拟器绕过XP框架
  • Oracle VM 设置CentOS7网络
  • lua代码解析1
  • C++特性之构造函数,析构函数和虚析构函数
  • 走进Linux的世界:gdb的使用
  • SCADE One vs Scade 6 - CNN池化层建模对比
  • uniapp | u-waterfall实现瀑布流商品列表(支持筛选查询)
  • C++优选算法精选100道编程题(附有图解和源码)
  • 五分钟系列-nm工具
  • 【龙泽科技】新能源汽车空调系统结构原理仿真教学软件
  • 设计一个图片上传服务,支持每秒5000张图片上传,并且要实时生成多种尺寸的缩略图。你觉得架构设计的要点有哪些?
  • NLP:Transformer优势详解
  • 基于SpringBoot+Vue的民宿管理系统(WebSocket及时通讯、腾讯地图API、支付宝沙盒支付、ECharts图形化分析)
  • Git版本管理工具入门及常用命令讲解---小白版
  • 芯脉:面向高速接口的SoC架构与完整性设计<2-2>
  • Go基础:Go语言流程控制详解
  • 【硬件-笔试面试题-103】硬件/电子工程师,笔试面试题(知识点:项目当中无人机的控制是怎么实现的)
  • 融智学的信息科学与智能科学(信智科学)基础研究
  • PyTorch 容器类详解:nn.Sequential、nn.ModuleList 与 nn.ModuleDict
  • 基于规则的专家系统对自然语言处理深层语义分析的影响与启示综合研究报告
  • 微服务配置管理
  • WinDivert学习文档之五-————编程API(七)
  • 【StarRocks】-- 异步物化视图实战