282. 给表达式添加运算符
方法一:递归 eval
class Solution:
def addOperators(self, num: str, target: int) -> List[str]:
def dfs(exp, i, pre):
# exp: 表达式,i:索引,pre:前一个数(串)。
if i == len(num):
if eval(exp) == target: # 通过 eval 计算表达式的值,速度很慢。
res.append(exp)
return
for op in ["+", "-", "*"]:
dfs(exp + op + num[i], i+1, num[i])
if pre[0] != '0': # 直接连接前面的数字,不能有前导 0
dfs(exp + num[i], i+1, pre + num[i])
res = []
dfs(num[0], 1, num[0])
return res
方法二:递归
n = len(num) ,构建表达式,可以往 num 中间的 n−1 个空隙添加 + 号、- 号或 * 号,或者不添加符号。
用「回溯法」来模拟这个过程。从左向右构建表达式,并实时计算表达式的结果。由于乘法运算优先级高于加法和减法运算,还需要保存最后一个连乘串(如 234)的运算结果。
定义递归函数 backtrack(expr,i,res,mul),其中:
- expr 为当前构建出的表达式;
- i 表示当前的枚举到了 num 的第 i 个数字;
- res 为当前表达式的计算结果;
- mul 为表达式最后一个连乘串的计算结果。
该递归函数分为两种情况:
如果 i=n,说明表达式已经构造完成,若此时有 res=target,则找到了一个可行解,将 expr 放入答案数组中,递归结束;
如果 i<n,需要枚举当前表达式末尾要添加的符号(+ 号、- 号或* 号),以及该符号之后需要截取多少位数字。设该符号之后的数字为 \val,按符号分类讨论:
- 若添加 + 号,则 res 增加 val,且 val 单独组成表达式最后一个连乘串;
- 若添加 - 号,则 res 减少val,且 −val 单独组成表达式最后一个连乘串;
- 若添加 * 号,由于乘法运算优先级高于加法和减法运算,我们需要对 res 撤销之前 mul 的计算结果,并添加新的连乘结果 mul∗val,也就是将 res 减少 mul 并增加 mul∗val。
代码实现时,为避免字符串拼接所带来的额外时间开销,采用字符数组的形式来构建表达式。此外,运算过程中可能会产生超过 32 位整数的结果,我们要用 64 位整数存储中间运算结果。
class Solution:
def addOperators(self, num: str, target: int) -> List[str]:
def dfs(exp, i, pre, cur):
if i == (m:=len(num)):
if cur == target:
res.append(exp)
return
for j in range(1, m-i+1):
t = num[i:i+j]
if t[0] == "0" and len(t) > 1: return # break
n = int(t)
if i == 0:
dfs(t, j, n, n)
continue
dfs(exp+'+'+t, i+j, n, cur+n)
dfs(exp+'-'+t, i+j, -n, cur-n)
dfs(exp+'*'+t, i+j, pre*n, cur-pre + pre*n)
res = []
dfs("", 0, 0, 0)
return res
295. 数据流的中位数
方法一:有序插入
from sortedcontainers import SortedList
class MedianFinder:
def __init__(self):
#self.nums = []
self.nums = SortedList()
def addNum(self, num: int) -> None:
# 插入后排序
#self.nums.append(num)
#self.nums.sort()
# 使用 bisect 有序插入
#bisect.insort(self.nums,num)
# 二分有序插入
#n = len(self.nums)
#l, r = 0, n
# while l < r:
# mid = (l + r)//2
# if num > self.nums[mid]:
# l = mid + 1
# else:
# r = mid
#self.nums.insert(r,num)
# 使用 SortedList
self.nums.add(num)
def findMedian(self) -> float:
# n = len(self.nums)
return self.nums[n//2] if (n:=len(self.nums))%2 else (self.nums[n//2]+self.nums[n//2-1]) / 2
方法二:对顶堆
大根堆维护比中位数小的数,小根堆维护比中位数大的数。
若 num 比中位数大(小根堆堆顶元素),则将其插入小根堆,插入后若小根堆长度大于大根堆长度,则将其堆顶转移到大根堆;否则将其插入大根堆。保证大根堆长度比小根堆长度多1或者相等。中位数要么在大根堆堆顶,要么就是取两个堆的堆顶求平均。
from sortedcontainers import SortedList
class MedianFinder:
def __init__(self):
"""
initialize your data structure here.
"""
self.lo = [] # 大根堆
self.hi = [] # 小根堆
def addNum(self, num: int) -> None:
heapq.heappush(self.lo, -num)
heapq.heappush(self.hi, -heapq.heappop(self.lo))
if len(self.lo) < len(self.hi):
heapq.heappush(self.lo, -heapq.heappop(self.hi))
def findMedian(self) -> float:
return -self.lo[0] if len(self.lo) > len(self.hi) else (-self.lo[0] + self.hi[0]) / 2
class MedianFinder:
def __init__(self):
self.left = []
self.right = []
def addNum(self, num: int) -> None:
l, r, = self.left, self.right
if not l or num <= -l[0]: # left 为空或 num <= 中位数(大根堆堆顶元素)
heapq.heappush(l, -num)
if len(l) - len(r) > 1:
heapq.heappush(r, -heapq.heappop(l)) # 转移小堆顶到大堆
else:
heapq.heappush(r, num)
if len(r) > len(l):
heapq.heappush(l, -heapq.heappop(r))
def findMedian(self) -> float:
return -self.left[0] if len(self.left) > len(self.right) else (-self.left[0]+self.right[0])/2
300. 最长递增子序列
方法一:动态规划
状态定义: dp[i] 是以 nums[i] 结尾的最长子序列长度。
转移方程:
dp[i] = max(dp[i], dp[j] + 1) for j in range(i) if nums[i] > nums[j]
说明:x in nums 在 x 前面所有小于 x 的元素中,找最大 dp。
初始状态:
dp = [1] * len(nums)
返回值:
dp 列表最大值
nums = [0,1,0,3,2,3]
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums: return 0
n = len(nums)
dp = [1] * n
for i, x in enumerate(nums):
for j in range(i):
if x > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
方法二:贪心 + 二分查找
简单的贪心,要使上升子序列尽可能的长,则需上升尽可能慢,每次加上的数尽可能的小。
nums = [3,5,6,2,5,4,19,5,6,7,12]
res = [3,5,6]
对 2 找到大于 2 的最小值 3 替换,res = [2,5,6]
对 5 前面正好 包含 跳过
对 4 找到大于 4 的最小值 5 替换,res = [2,4,6]
res = [2,4,6,19]
对 5 找到大于 5 的最小值 6 替换,res = [2,4,5,19]
对 6 找到大于 6 的最小值 19 替换,res = [2,4,5,6]
res = 【2,4,5,6,7,12】
nums = [4,10,4,3,8,9],[4,10],[3,10],【3,8,9】
nums = [10,9,2,5,3,7,101,18],[10],[9],[2,5],[2,3],[2,3,7],[2,3,7,101],【2,3,7,18】
nums = [4,4,4,4,4],【4】
nums = [0,1,0,3,2,3],[0,1],[0,1,3],[0,1,2],【0,1,2,3】
nums = [1,3,6,7,9,4,10,5,6],[1,3,6,7,9],[1,3,4,7,9],[1,3,4,7,9,10],[1,3,4,5,9,10],[1,3,4,5,6,10],【1, 3, 4, 5, 6, 10】
注意:res 不一定是最长递增子序列
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums: return 0
res, lengh = [nums[0]], 1
for x in nums[1:]:
if x > res[-1]:
res.append(x)
lengh += 1
else:
idx = bisect.bisect_left(res,x)
if res[idx] != x:
res[idx] = x # 替换会影响 x > res[-1] 条件的判断
return lengh