DP 题目三要素:状态,转移,初始化
二维 DP:
python
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
# dp[i][j]: text1 前 i 个字符与 text2 前 j 个字符的 LCS 长度
# 多开一行一列的哨兵表示空串,初始化 0 恰好正确
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i - 1] == text2[j - 1]: # 注意偏移:前 i 个的末尾是下标 i-1
dp[i][j] = dp[i - 1][j - 1] + 1 # 末尾配对,双方各退一格
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) # 丢掉某一方的末尾,取更优
return dp[m][n]
时空复杂度:O(mn);
一句话思路:用 dp[i][j] 表示 text1 前 i 个字符与 text2 前 j 个字符的最长公共子序列长度,并将空串对应的边界初始化为 0。遍历两个字符串,末尾字符相同时取 dp[i-1][j-1]+1,否则取 dp[i-1][j] 和 dp[i][j-1] 的较大值,最终 dp[m][n] 即为答案。
很清晰的讲解:leetcode-动态规划篇 64题 最小路径和_哔哩哔哩_bilibili
python
class Solution:
def minPathSum(self, grid: list[list[int]]) -> int:
m,n=len(grid),len(grid[0])
dp=[[0]*n for _ in range(m)]
dp[0][0] = grid[0][0]
for j in range(1,n):
dp[0][j] = dp[0][j-1]+grid[0][j]
for i in range(1,m):
dp[i][0] = dp[i-1][0]+grid[i][0]
for i in range(1,m):
for j in range(1,n):
dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j]
return dp[m-1][n-1]
一维DP:
python
class Solution:
def climbStairs(self, n: int) -> int:
if n<=2:
return n
a,b = 1,2
for _ in range(3,n+1):
a,b = b,a+b
return b
python
class Solution:
def rob(self, nums: List[int]) -> int:
prev,curv = 0,0
for num in nums:
prev,curv = curv,max(curv,num+prev)
return curv
一句话总结:用滚动变量动态规划遍历每间房屋,在"不偷当前房屋(curv)"和"偷当前房屋(num + prev)"之间取较大值,最终 curv 就是不偷相邻房屋时能获得的最高金额。
上述两道一维DP的题目,时间复杂度均为O(N),空间复杂度均为O(1)。
300. 最长递增子序列 - 力扣(LeetCode)非常爱考
python
class Solution:
def lengthOfLIS(self, nums: list[int]) -> int:
n=len(nums)
dp=[1]*n
for i in range(n):
for j in range(i):
if nums[j]<nums[i]:
dp[i]=max(dp[j]+1,dp[i])
return max(dp)
一句话总结:用 dp[i] 表示以 nums[i] 结尾的最长递增子序列长度,每次将当前元素接在前面较小的元素之后并取最优的 dp[j]+1,最终所有 dp 值中的最大值即为答案。
贪心:
python
class Solution:
def maxProfit(self, prices: list[int]) -> int:
price=inf
profit=0
for i in prices:
price=min(price,i)
profit=max(profit,i-price)
return profit
非常清晰的讲解:跳跃游戏:别管怎么跳,只记最远能到哪 | LeetCode Hot 100 · 078_哔哩哔哩_bilibili
python
class Solution:
def canJump(self, nums: list[int]) -> bool:
far = 0
for i,t in enumerate(nums):
if far<i:
return False
else:
far = max(far,t+i)
return True
python
# 分发糖果 - 贪心
class Solution:
def candy(self, ratings: List[int]) -> int:
n = len(ratings)
candies = [1] * n # 每个孩子至少1颗糖果,初始值保证满足下限约束
# 从左到右:评分比左邻居高,就比左邻居多1颗
for i in range(1, n):
if ratings[i] > ratings[i - 1]:
candies[i] = candies[i - 1] + 1
# 从右到左:评分比右邻居高,取两个方向约束的较大值
for i in range(n - 2, -1, -1):
if ratings[i] > ratings[i + 1]:
candies[i] = max(candies[i], candies[i + 1] + 1)
return sum(candies)
堆和栈:
python
class Solution:
def isValid(self, s: str) -> bool:
pairs = {')':'(',']':'[','}':'{'}
stack=[]
for ch in s:
if ch in pairs:
if not stack or stack.pop() != pairs[ch]:
return False
else:
stack.append(ch)
# 注意此处的返回,最易漏掉
return not stack
python
class Solution:
2 def dailyTemperatures(self, temperatures: list[int]) -> list[int]:
3 n=len(temperatures)
4 res=[0]*
n
5 stack=[]
6 for i,t in enumerate(temperatures):
7 while stack and t>temperatures[stack[-1]]:
8 j=stack.pop()
9 res[j]=i-
j
10 stack.append(i)
11 return res
215. 数组中的第K个最大元素 - 力扣(LeetCode)小顶堆求解,堆-heap
# heap - 堆,heapq - 引入小顶堆
import heapq
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
heap = []
for x in nums:
heapq.heappush(heap,x)
if len(heap)>k:
heapq.heappop(heap)
return heap[0]
二分查找:(模板)
python
# 二分查找 - 二分查找
class Solution:
def search(self, nums: List[int], target: int) -> int:
lo, hi = 0, len(nums) - 1 # 闭区间 [lo, hi]
while lo <= hi: # 区间非空才继续
mid = (lo + hi) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
lo = mid + 1 # 目标在右半段
else:
hi = mid - 1 # 目标在左半段
return -1 # 区间为空,没找到