目录
39.组合总和
注意:
set不能添加list得加元组
元组不可修改
sorted后得赋值
python
class Solution:
def combinationSum(self, candidates,target):
ans=set()
def fenjie(num,se):
if num==0:
se=tuple(sorted(se))
ans.add(se)
return
if num<0:
return
for i in candidates:
se2=tuple(list(se)+[i])
fenjie(num-i,se2)
fenjie(target,())
return [list(x) for x in ans] # 转换为题目要求的格式
if __name__=='__main__':
candidates = [2,3,6,7];target = 7
sol=Solution()
ans=sol.combinationSum(candidates,target)
print(ans)
虽然过了,但这道题更好的办法是
回溯(单向剪枝)
回溯是一种 试探+撤销 的策略
我们将这问题看成一颗搜索树,每个节点表示一种选择路径,那么我们从根节点开始一步步试探性地往下走,直到走到满足条件的叶子节点 ,那么就把路径加入答案
我们用start控制开始下标,单向(升序)地往后面选(注意同一个数字可以重复使用,那么下一层就是从当前start开始
python
start=0, path=[], remaining=7
├── 选2 → path=[2], remaining=5
│ ├── 选2 → path=[2,2], remaining=3
│ │ ├── 选2 → path=[2,2,2], remaining=1
│ │ │ └── 选2 → path=[2,2,2,2], remaining=-1 ❌ 超了,返回
│ │ │ └── 选3 → path=[2,2,2,3], remaining=-2 ❌ 超了,返回
│ │ ├── 选3 → path=[2,2,3], remaining=0 ✅ 找到结果
│ │ └── ...
│ └── 选3 → path=[2,3], remaining=2
├── 选3 → path=[3], remaining=4
│ ├── 选3 → path=[3,3], remaining=1
│ └── ...
├── 选6 → path=[6], remaining=1
├── 选7 → path=[7], remaining=0 ✅ 找到结果
python
class Solution:
def combinationSum(self, candidates, target):
res = []
candidates.sort()
def backtrack(start, path, remaining):
if remaining == 0:
res.append(path[:]) # 找到一个合法组合,加入结果
return
if remaining < 0:
return # 超过了目标值,剪枝
for i in range(start, len(candidates)):
path.append(candidates[i])
backtrack(i, path, remaining - candidates[i]) # 可重复选,从 i 开始
path.pop() # 撤销上一步选择,继续尝试下一个
backtrack(0, [], target)
return res
其中 path,pop() 有点像 恢复vis[ ] = 0
3102.最小化曼哈顿距离
本来我想用一半的二维矩阵来优化空间复杂度,但是这样反而得不偿失了,太绕了 连正确答案都跑不出来,还是正常用二维的
这题的主要思路的思路就是枚举删的点,然后再枚举所有点的所有边(除去当时删的点的),进行表示
python
'''
class Solution:
def minimumDistance(self, points):
n=len(points)
ma=[[0]*i for i in range(n-1,0,-1)]
for i in range(n-1):
for j in range(i+1,n):
dis=abs(points[i][0]-points[j][0])+abs(points[i][1]-points[j][1])
ma[i][j-i-1]=dis
print(ma)
ans=float('inf')
for i in range(n):#去掉的点
now=-1
for j in range(n):#除去掉的点外的点出发到其他点的距离
if j==i:
continue
else:
for a in range(n-j-1):
if a+1==i:continue
now=max(now,ma[j][a])
for b in range(j):
if b==i:continue
now=max(now,ma[b][j-b-1])
ans=min(ans,now)
return ans
'''
class Solution:
def minimumDistance(self, points):
import itertools
n = len(points)
ans = float('inf')
for skip in range(n): # 枚举删除哪一个点
max_dist = 0
remaining = [points[i] for i in range(n) if i != skip]
for i in range(len(remaining)):
for j in range(i + 1, len(remaining)):
x1, y1 = remaining[i]
x2, y2 = remaining[j]
dist = abs(x1 - x2) + abs(y1 - y2)
max_dist = max(max_dist, dist)
ans = min(ans, max_dist)
return ans
if __name__=='__main__':
points=[[3,10],[5,15],[10,2],[4,4]]
sol=Solution()
ans=sol.minimumDistance(points)
print(ans)
但是这样暴力肯定会TLE
利用曼哈顿距离的性质

python
class Solution:
def minimumDistance(self, points):
n = len(points)
vals = [[] for _ in range(4)] # 保存四种变换
for i, (x, y) in enumerate(points):
vals[0].append((x + y, i))
vals[1].append((x - y, i))
vals[2].append((-x + y, i))
vals[3].append((-x - y, i))
candidates = set()
for arr in vals:
arr.sort()
candidates.add(arr[0][1]) # 最小值对应点
candidates.add(arr[-1][1]) # 最大值对应点
def get_max_dist(skip):
max_val = 0
for arr in vals:
temp = [(val, idx) for val, idx in arr if idx != skip]
max_val = max(max_val, temp[-1][0] - temp[0][0])
return max_val
return min(get_max_dist(i) for i in candidates)
3163.压缩字符串3
主要用到 strip
python
class Solution:
def compressedString(self, word: str) -> str:
comp=''
l=len(word)
while word:
'''
i=0
while word[i]==word[i+1]:#前缀
i+=1
comp+=str(i+1)+word[i]
'''
c=word[0]
word=word.lstrip(c)# lstrip : 删除左边连续子串直到碰到其他字符
#print(word)
l2=len(word)
d=l-l2
if d>9:
d1=d//9
d2=d%9
comp+=('9'+c)*d1
if d2:
comp+=str(d2)+c
else:
comp+=str(d)+c
l=l2
return comp
if __name__=='__main__':
word='abcde'
sol=Solution()
ans=sol.compressedString(word)
print(ans)
灵神的代码
其中enumerate是同时提取索引和元素(素数筛里面用的也是enumerate)
divmod( a,b )返回( a//b,a%b )
python
class Solution:
def compressedString(self, word: str) -> str:
t = []
i0 = -1
for i, c in enumerate(word):
if i + 1 == len(word) or c != word[i + 1]:
k, rem = divmod(i - i0, 9)
t.append(("9" + c) * k)
if rem:
t.append(str(rem))
t.append(c)
i0 = i
return ''.join(t)
作者:灵茶山艾府
链接:https://leetcode.cn/problems/string-compression-iii/solutions/2790666/mo-ni-pythonjavacgo-by-endlesscheng-3hk7/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
46.全排列
注意itertools.permutations出来的是元组,得list
python
from itertools import permutations
class Solution:
def permute(self, nums):
l=len(nums)
ans=permutations(nums,l)
lis=[]
for i in ans:
lis.append(list(i))
return lis
if __name__=='__main__':
nums=[1,2,3]
sol=Solution()
ans=sol.permute(nums)
print(ans)
53.最大子数组和
我的第一想法是用前缀和之差,于是我直接暴力表示了
python
class Solution:
def maxSubArray(self, nums):
pre=[[0,0]]
l=len(nums)
for i,k in enumerate(nums):
#print(i,k)
pre.append([pre[-1][0]+k,pre[-1][1]+1])
#print(pre)
ans=-float('inf')
for su,n in pre:
for j in range(n):
now=su-pre[j][0]
ans=max(ans,now)
return ans
if __name__=='__main__':
nums=[-2,1,-3,4,-1,2,1,-5,4]
sol=Solution()
ans=sol.maxSubArray(nums)
print(ans)
因为 最大值= -最小值,所以我们其实可以在一边遍历数组计算前缀和的同时一边维护前缀和的最小值
请注意,由于题目要求子数组不能为空,应当先计算前缀和-最小前缀和,再更新最小前缀和。相当于不能在同一天买入股票又卖出股票。如果先更新最小前缀和,再计算前缀和-最小前缀和,就会把空数组的元素和 0 算入答案。(来自灵神)
python
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
ans = -inf
min_pre_sum = pre_sum = 0
for x in nums:
pre_sum += x # 当前的前缀和
ans = max(ans, pre_sum - min_pre_sum) # 减去前缀和的最小值
min_pre_sum = min(min_pre_sum, pre_sum) # 维护前缀和的最小值
return ans