1.爬楼梯
问题:
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
方法:动态规划
f(n)=f(n-1)+f(n-2)
# 爬楼梯:动态规划
# f(n)=f(n-1)+f(n-2) --> 每一级台阶是前两级台阶之和,以此类推,算出最终的
def ClimbStairs(n):
if n <= 2:
return n
p_pre, pre, res = 1, 2, 0
for num in range(3, n+1):
res = p_pre + pre
p_pre, pre = pre, res
return res
print(ClimbStairs(5))
2.杨辉三角
问题:
给定一个非负整数 numRows
,生成「杨辉三角」的前 numRows
行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
方法1:创建三角形 再赋值
def generate(numRows):
tri = []
# 创建三角形
for i in range(1, numRows+1):
row = []
for j in range(i):
row.append(1)
tri.append(row)
# 开始赋值
for i in range(2, numRows):
for j in range(1, i):
tri[i][j] = tri[i-1][j-1] + tri[i-1][j]
return tri
方法2:逐行动态扩展(简洁)
def generate(numRows):
tri = []
for i in range(numRows):
row = [1] * (i+1) # 新建行
for j in range(1, i): # 定在需要改的范围内
row[j] = tri[i-1][j-1] + tri[i-1][j] # 直接改变该行
tri.append(row) # 该行加入结果
return tri
方法3:简洁(创建复杂)
初始化全1数组,预分配内存
# 方法1:初始化(预分配内存)
def Triangle(numRows):
triangle = [[1 for _ in range(i)] for i in range(1, numRows+1)] # 初始化(要用循环,不能直接乘!!!)
for i in range(2, numRows):
for j in range(1, i):
triangle[i][j] = triangle[i-1][j-1] + triangle[i-1][j]
return triangle
3.打家劫舍
问题:
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
方法1:动态规划(dp数组)
# 方法1:动态规划(dp数组)
def rob(nums):
if len(nums) <= 2:
return max(nums)
dp = [0 for _ in range(len(nums))]
dp[0], dp[1] = nums[0], max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(dp[i-2]+nums[i], dp[i-1])
return dp[-1]
方法2:动态规划(空间优化)
# 方法2:动态规划(空间优化)
def rob(nums):
if len(nums) <= 2:
return max(nums)
p_prev, prev = nums[0], max(nums[0], nums[1])
res = 0
for i in range(2, len(nums)):
res = max(p_prev+nums[i], prev)
p_prev, prev = prev, res
return res
4.完全平方数
问题:
给你一个整数 n
,返回 和为 n
的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。
方法:逐点dp
从前面的点依次记录步数,到第n点时,也是最终结果。
def numSquares(n):
dp = [n] * (n+1) # 记录最少步数
dp[0] = 0
for i in range(1, n+1):
j = 1
# 遍历完全平方
while j * j <= i:
square = j * j
dp[i] = min(dp[i], dp[i-square]+1) # 更新最少步数
j += 1
return dp[-1]
5.零钱兑换
问题:
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
方法:逐点dp
def coinChange(coins, amount):
dp = [float('inf')] * (amount+1) # 设置一个很大的值,表示凑不出来
dp[0] = 0
for i in range(1, amount+1): # 逐点
for coin in coins: # 遍历零钱
if coin <= i: # 零钱不能超过该值
dp[i] = min(dp[i], dp[i-coin]+1) # 记录最少零钱数
return dp[-1] if dp[-1] <= amount else -1 # 返回最终结果(无的话就是-1)
6.单词拆分
问题:
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s
则返回 true
。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
方法1: 逐点dp -- 逐词匹配
# 逐点dp -- 逐词匹配
# 逐个分析每个字符能否被组成
def wordBreak(s, wordDict):
dp = [False] * (len(s)+1)
dp[0] = True
# word_set = set(wordDict)
for i in range(1, len(s)+1):
for word in wordDict: # 逐词检查是否符合(检查:1.dp[i-L] 2.是否匹配)
L = len(word)
if i >= L and dp[i-L] and s[i-L:i] == word:
dp[i] = True
return dp[-1]
方法2: 逐点dp -- 逐点枚举
# 逐点dp -- 逐点枚举
def wordBreak(s, wordDict):
dp = [False] * (len(s)+1)
dp[0] = True
word_set = set(wordDict)
for i in range(1, len(s)+1):
for j in range(i): # 逐点枚举是否符合(检查:1.dp[j] 2.是否匹配)
if dp[j] and s[j:i] in word_set:
dp[i] = True
return dp[-1]