有效的数独
Date:2020-10-05 周一
class Solution:def isHas(self,board:List[str]) ->bool:valueList = [0,0,0,0,0,0,0,0,0]for val in board:if val == ".":passelif int(val) > 9 or int(val) < 1:return Falseelse:val = int(val) - 1if(valueList[val] != 0):return FalsevalueList[val] = 1return Truedef isValidSudoku(self, board: List[List[str]]) -> bool:#检查行是否符合for i in range(9):result = self.isHas(board[i])if not result:return False#检查列是否符合for j in range(9):vauleList = []for i in range(9):vauleList.append(board[i][j])result = self.isHas(vauleList)if not result:return False#检查3x3矩阵是否符合index = 0while index < 9:vauleList = []mid = index % 3rows = range(mid * 3,(mid + 1) * 3)if index < 3 :cols = range(0,3)elif index < 6 :cols = range(3,6)else:cols = range(6,9)for i in rows:for j in cols:vauleList.append(board[i][j])result = self.isHas(vauleList)if not result:return Falseindex +=1return True
官方解法-一次遍历

class Solution:def isValidSudoku(self, board):""":type board: List[List[str]]:rtype: bool"""# init datarows = [{} for i in range(9)]columns = [{} for i in range(9)]boxes = [{} for i in range(9)]# validate a boardfor i in range(9):for j in range(9):num = board[i][j]if num != '.':num = int(num)box_index = (i // 3 ) * 3 + j // 3# keep the current cell valuerows[i][num] = rows[i].get(num, 0) + 1columns[j][num] = columns[j].get(num, 0) + 1boxes[box_index][num] = boxes[box_index].get(num, 0) + 1# check if this value has been already seen beforeif rows[i][num] > 1 or columns[j][num] > 1 or boxes[box_index][num] > 1:return Falsereturn True
解数独**
递归

class Solution:def solveSudoku(self, board: List[List[str]]) -> None:def dfs(pos: int):nonlocal valid #nonlocal关键字用来在函数或其他作用域中使用外层(非全局)变量。if pos == len(spaces):valid = Truereturni, j = spaces[pos]for digit in range(9):if line[i][digit] == column[j][digit] == block[i // 3][j // 3][digit] == False:line[i][digit] = column[j][digit] = block[i // 3][j // 3][digit] = Trueboard[i][j] = str(digit + 1)dfs(pos + 1)line[i][digit] = column[j][digit] = block[i // 3][j // 3][digit] = Falseif valid:returnline = [[False] * 9 for _ in range(9)]column = [[False] * 9 for _ in range(9)]block = [[[False] * 9 for _a in range(3)] for _b in range(3)]valid = Falsespaces = list()for i in range(9):for j in range(9):if board[i][j] == ".":spaces.append((i, j))else:digit = int(board[i][j]) - 1line[i][digit] = column[j][digit] = block[i // 3][j // 3][digit] = Truedfs(0)
组合总和**
回溯+剪枝




class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:def dfs(candidates,begin,size,path,res,target):if target < 0:returnif target == 0:res.append(path)returnfor index in range(begin,size):dfs(candidates,index,size,path+[candidates[index]],res,target - candidates[index]) #[1, 2] + [3] 语法生成了新的列表size = len(candidates)if size == 0:return []path = []res = []dfs(candidates,0,size,path,res,target)return res
组合总和Ⅱ**
自己写的半吊子回溯
class Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:candidates.sort()def dfs(candidates,begin,end,path,res,used,target):if target < 0:returnif target == 0:res.add(str(path))# res.append(path)returnfor i in range(begin,end):if used[i] == False:used[i] = Truedfs(candidates,i,end,path + [candidates[i]],res,used,target - candidates[i])used[i] = Falseend = len(candidates)if end == 0:return []used = [False] * endpath = []res = set()dfs(candidates,0,end,path,res,used,target)result = []for val in res:val = val[1:len(val)-1]midList = []for mid in val.split(","):midList.append(int(mid))result.append(midList)return result
大佬思路(重点是去重)




from typing import Listclass Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:def dfs(begin, path, residue):if residue == 0:res.append(path[:])returnfor index in range(begin, size):if candidates[index] > residue:breakif index > begin and candidates[index - 1] == candidates[index]:continuepath.append(candidates[index])dfs(index + 1, path, residue - candidates[index])path.pop()size = len(candidates)if size == 0:return []candidates.sort()res = []dfs(0, [], target)return res
缺失的第一个正数*
又又没看到题目时间复杂度要求
class Solution:def firstMissingPositive(self, nums: List[int]) -> int:nums.sort()index = 0while index < len(nums) and nums[index] <= 0: index += 1num = 1while index < len(nums) and nums[index] == num:if index != len(nums) - 1 and nums[index] == nums[index + 1]:index += 1continuenum += 1index += 1return num
哈希表


class Solution:def firstMissingPositive(self, nums: List[int]) -> int:n = len(nums)for i in range(n):if nums[i] <= 0:nums[i] = n + 1for i in range(n):num = abs(nums[i])if num <= n:nums[num - 1] = -abs(nums[num - 1])for i in range(n):if nums[i] > 0:return i + 1return n + 1
接雨水**
暴力(会超时)

class Solution:def trap(self, height: List[int]) -> int:ans = 0for i in range(len(height)):max_left, max_right = 0,0# 寻找 max_leftfor j in range(0,i):max_left = max(max_left,height[j])# 寻找 max_rightfor j in range(i,len(height)):max_right = max(max_right,height[j])if min(max_left,max_right) > height[i]:ans += min(max_left,max_right) - height[i]return ans
动态规划



class Solution:def trap(self, height: List[int]) -> int:# 边界条件if not height: return 0n = len(height)maxleft = [0] * nmaxright = [0] * nans = 0# 初始化maxleft[0] = height[0]maxright[n-1] = height[n-1]# 设置备忘录,分别存储左边和右边最高的柱子高度for i in range(1,n):maxleft[i] = max(height[i],maxleft[i-1])for j in range(n-2,-1,-1):maxright[j] = max(height[j],maxright[j+1])# 一趟遍历,比较每个位置可以存储多少水for i in range(n):if min(maxleft[i],maxright[i]) > height[i]:ans += min(maxleft[i],maxright[i]) - height[i]return ans
双指针


class Solution:def trap(self, height: List[int]) -> int:# 边界条件if not height: return 0n = len(height)left,right = 0, n - 1 # 分别位于输入数组的两端maxleft,maxright = height[0],height[n - 1]ans = 0while left <= right:maxleft = max(height[left],maxleft)maxright = max(height[right],maxright)if maxleft < maxright:ans += maxleft - height[left]left += 1else:ans += maxright - height[right]right -= 1return ans
字符串相乘**
无视要求做。。。
class Solution:def getNum(self,num:str)->int:return {"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}.get(num,-1)def multiply(self, num1: str, num2: str) -> str:numInt1 = numInt2 = 0index1 , index2 = (len(num1) - 1),(len(num2) - 1)w = 0while index1 >= 0:numInt1 += self.getNum(num1[index1]) * (10 ** w)w += 1index1 -= 1w = 0while index2 >= 0:numInt2 += self.getNum(num2[index2]) * (10 ** w)w += 1index2 -= 1print(numInt1," ",numInt2)return str(numInt1 * numInt2)
做加法

class Solution:def multiply(self, num1: str, num2: str) -> str:if num1 == "0" or num2 == "0":return "0"ans = "0"m, n = len(num1), len(num2)for i in range(n - 1, -1, -1):add = 0y = int(num2[i])curr = ["0"] * (n - i - 1)for j in range(m - 1, -1, -1):product = int(num1[j]) * y + addcurr.append(str(product % 10))add = product // 10if add > 0:curr.append(str(add))curr = "".join(curr[::-1])ans = self.addStrings(ans, curr)return ansdef addStrings(self, num1: str, num2: str) -> str:i, j = len(num1) - 1, len(num2) - 1add = 0ans = list()while i >= 0 or j >= 0 or add != 0:x = int(num1[i]) if i >= 0 else 0y = int(num2[j]) if j >= 0 else 0result = x + y + addans.append(str(result % 10))add = result // 10i -= 1j -= 1return "".join(ans[::-1])
做乘法





class Solution:def multiply(self, num1: str, num2: str) -> str:if num1 == "0" or num2 == "0":return "0"m, n = len(num1), len(num2)ansArr = [0] * (m + n)for i in range(m - 1, -1, -1):x = int(num1[i])for j in range(n - 1, -1, -1):ansArr[i + j + 1] += x * int(num2[j])for i in range(m + n - 1, 0, -1):ansArr[i - 1] += ansArr[i] // 10ansArr[i] %= 10index = 1 if ansArr[0] == 0 else 0ans = "".join(str(x) for x in ansArr[index:])return ans
通配符匹配**
Date:2020-10-10 周六
好家伙。。。果然直接硬解是不行的。。。只想到了是与T10的正则表达式类似的题,可能需要用DP,但是没敢尝试。。。
错误代码!!警示自己
贪心算法

// 我们用 sIndex 和 pIndex 表示当前遍历到 s 和 p 的位置// 此时我们正在 s 中寻找某个 u_i// 其在 s 和 p 中的起始位置为 sRecord 和 pRecord// sIndex 和 sRecord 的初始值为 0// 即我们从字符串 s 的首位开始匹配sIndex = sRecord = 0// pIndex 和 pRecord 的初始值为 1// 这是因为模式 p 的首位是星号,那么 u_1 的起始位置为 1pIndex = pRecord = 1while sIndex < s.length and pIndex < p.length doif p[pIndex] == '*' then// 如果遇到星号,说明找到了 u_i,开始寻找 u_i+1pIndex += 1// 记录下起始位置sRecord = sIndexpRecord = pIndexelse if match(s[sIndex], p[pIndex]) then// 如果两个字符可以匹配,就继续寻找 u_i 的下一个字符sIndex += 1pIndex += 1else if sRecord + 1 < s.length then// 如果两个字符不匹配,那么需要重新寻找 u_i// 枚举下一个 s 中的起始位置sRecord += 1sIndex = sRecordpIndex = pRecordelse// 如果不匹配并且下一个起始位置不存在,那么匹配失败return Falseend ifend while// 由于 p 的最后一个字符是星号,那么 s 未匹配完,那么没有关系// 但如果 p 没有匹配完,那么 p 剩余的字符必须都是星号return all(p[pIndex] ~ p[p.length - 1] == '*')

class Solution:def isMatch(self, s: str, p: str) -> bool:def allStars(st: str, left: int, right: int) -> bool:return all(st[i] == '*' for i in range(left, right))def charMatch(u: str, v: str) -> bool:return u == v or v == '?'sRight, pRight = len(s), len(p)while sRight > 0 and pRight > 0 and p[pRight - 1] != '*':if charMatch(s[sRight - 1], p[pRight - 1]):sRight -= 1pRight -= 1else:return Falseif pRight == 0:return sRight == 0sIndex, pIndex = 0, 0sRecord, pRecord = -1, -1while sIndex < sRight and pIndex < pRight:if p[pIndex] == '*':pIndex += 1sRecord, pRecord = sIndex, pIndexelif charMatch(s[sIndex], p[pIndex]):sIndex += 1pIndex += 1elif sRecord != -1 and sRecord + 1 < sRight:sRecord += 1sIndex, pIndex = sRecord, pRecordelse:return Falsereturn allStars(p, pIndex, pRight)
跳跃游戏Ⅱ*
暴力递归(超时)
class Solution:def jump(self, nums: List[int]) -> int:ans = -1def dfs(nums: List[int] , index: int, step: int):nonlocal ansif index == len(nums) - 1:ans = min(ans,step) if ans != -1 else stepreturnif index >= len(nums):returnfor i in range(nums[index],0,-1):dfs(nums,index + i,step + 1)returndfs(nums,0,0)return ans
贪心算法
反向查找出发位置
正向查找可到达的最大位置


class Solution:def jump(self, nums: List[int]) -> int:n = len(nums)maxPos, end, step = 0, 0, 0for i in range(n - 1):if maxPos >= i:maxPos = max(maxPos, i + nums[i])if i == end:end = maxPosstep += 1return step
全排列
递归
class Solution:def permute(self, nums: List[int]) -> List[List[int]]:ans = []def dfs(nums: List[int],numsUsed: List[bool],path: List[int]):nonlocal ansif len(path) == len(nums):ans.append(path)returnfor i in range(len(nums)):if numsUsed[i] == False:numsUsed[i] = Truedfs(nums,numsUsed,path + [nums[i]])numsUsed[i] = FalsereturnnumsUsed = [False] * len(nums)dfs(nums,numsUsed,[])return ans
全排列Ⅱ
递归+剪枝
class Solution:def permuteUnique(self, nums: List[int]) -> List[List[int]]:ans = []def dfs(nums: List[int],numsUsed: List[bool],path: List[int]):nonlocal ansif len(path) == len(nums):ans.append(path)returnfor i in range(len(nums)):if numsUsed[i] == False:# 剪枝if i > 0 and numsUsed[i - 1] == False and nums[i] == nums[i - 1]:continuenumsUsed[i] = Truedfs(nums,numsUsed,path + [nums[i]])numsUsed[i] = FalsereturnnumsUsed = [False] * len(nums)nums.sort() #先排序dfs(nums,numsUsed,[])return ans

剪枝是减去重复的树枝,只保留顺序的树枝,比如 [1,1,2] ,为了方便记做 [1,1’,2] 。只保留1 -> 1’ -> 2这样的顺序枝,而认为1’ -> 1 -> 2 这样的就是重复枝,把他剪掉。所以顺序枝的特点是遍历在重复的第二节点的时候其前一个数值相等的节点肯定遍历过。所以根据这个特点就可以精确剪枝了。
旋转图像
DFS + 递推公式
这道题就是需要找规律。旋转90度体现在坐标上就是(i , j) -> (j , n - i)。每个位置都会存在一个圈圈,每四个为一圈(中心点除外)。每次DFS去顺时针挪动这四个对应位置的值即可。
import math# (i , j) -> (j , n - i)class Solution:def rotate(self, matrix: List[List[int]]) -> None:if not matrix:returndef dfs(matrix: List[List[int]],i:int,j:int,step:int,val:int):mid = matrix[i][j]if val != -999:matrix[i][j] = valif step == 4:returndfs(matrix,j,(n - 1 - i),step + 1,mid)returnn = len(matrix)for i in range(0,math.ceil(n / 2)):for j in range(i,n - i - 1):dfs(matrix,i,j,0,-999)
字母异位词分组*
暴力(超时)
class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
def isMatch(str1:str,str2:str) -> bool: #匹配算法
if str1 == "" and str2 == "":
return True
elif str1 == "" or str2 == "":
return False
elif len(str1) != len(str2):
return False
for s in str1:
if str2.find(s) == -1:
return False
str2 = str2[0:str2.find(s)] + str2[str2.find(s) + 1 : ]
return True
def pushAns(ans:List[List[str]],strs:str):#分组算法
if not ans:
ans.append([strs])
else:
matched = False
for i in range(len(ans)):
if isMatch(ans[i][0],strs):
ans[i].append(strs)
matched = True
if not matched:
ans.append([strs])
ans = []
for val in strs:
pushAns(ans,val)
return ans
排序数组分类


class Solution:
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
ans = collections.defaultdict(list)
# defaultdict 标准字典包含setdefault()方法,用于检索值并在值不存在时建立默认值 缺少key会返回一个默认值 defaultdict允许调用者在容器初始化时指定默认的预先设置。
for s in strs:
ans[tuple(sorted(s))].append(s) # tuple(sorted(s)) -> ('a' , 'e' , 'r')
return list(ans.values())
按计数分类


class Solution:
def groupAnagrams(strs):
ans = collections.defaultdict(list)
for s in strs:
count = [0] * 26
for c in s:
count[ord(c) - ord('a')] += 1
ans[tuple(count)].append(s)
return ans.values()
Power(x,n)**
暴力(超时)
class Solution:
def myPow(self, x: float, n: int) -> float:
ans = 1.0
if n >= 0:
for _ in range(n):
ans *= x
else:
for _ in range(abs(n)):
ans *= (1 / x)
return ans if ans <= (2 ** 31 - 1) and ans >=(- 2 ** 31) else (2 ** 31 - 1) if ans > 0 else (-2 ** 31)
快速幂 + 递归

class Solution:
def myPow(self, x: float, n: int) -> float:
def quickMul(N):
if N == 0:
return 1.0
y = quickMul(N // 2)
return y * y if N % 2 == 0 else y * y * x
return quickMul(n) if n >= 0 else 1.0 / quickMul(-n)
快速幂 + 迭代

class Solution:
def myPow(self, x: float, n: int) -> float:
def quickMul(N):
ans = 1.0
# 贡献的初始值为 x
x_contribute = x
# 在对 N 进行二进制拆分的同时计算答案
while N > 0:
if N % 2 == 1:
# 如果 N 二进制表示的最低位为 1,那么需要计入贡献
ans *= x_contribute
# 将贡献不断地平方
x_contribute *= x_contribute
# 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
N //= 2
return ans
return quickMul(n) if n >= 0 else 1.0 / quickMul(-n)
N皇后 **
回溯+基于集合



class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
def generateBoard():
board = list()
for i in range(n):
row[queens[i]] = "Q"
board.append("".join(row))
row[queens[i]] = "."
return board
def backtrack(row: int):
if row == n:
board = generateBoard()
solutions.append(board)
else:
for i in range(n):
if i in columns or row - i in diagonal1 or row + i in diagonal2:
continue
queens[row] = i
columns.add(i)
diagonal1.add(row - i)
diagonal2.add(row + i)
backtrack(row + 1)
columns.remove(i)
diagonal1.remove(row - i)
diagonal2.remove(row + i)
solutions = list()
queens = [-1] * n
columns = set()
diagonal1 = set()
diagonal2 = set()
row = ["."] * n
backtrack(0)
return solutions

N皇后Ⅱ
回溯+基于集合
算法同上
螺旋矩阵*
模拟

class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix:return []
x=y=0 # 矩阵元素位置初始化
res = [] # 初始化,存储遍历后的矩阵元素
dx = [ 0, 1, 0,-1] # 方向:右,下,左,上
dy = [ 1, 0,-1, 0] # 注:与通常平面坐标系 记号 不同
di = 0 # 初始化方向变量
visited = set() # 初始化集合,存储已走过的坐标
m,n = len(matrix),len(matrix[0]) # 矩阵的行列
for i in range(m*n): #
res.append(matrix[x][y]) # 存储遍历矩阵过的元素
visited.add((x,y)) # 存储遍历过的坐标
tx,ty = x+dx[di],y+dy[di] # 先记录下一步坐标,用于判断下一步怎么走
if 0<=tx<m and 0<=ty<n and (tx,ty) not in visited: # 判断坐标是否需变向,且没有遍历过
x,y = tx,ty
else:
di = (di+1)%4 # 改变方向,右下左上为一圈,防止方向坐标越界
x,y = x + dx[di],y+dy[di] # 下一步坐标
return res
跳跃游戏**
暴力递归(超时)
class Solution:
def canJump(self, nums: List[int]) -> bool:
ans = False
def dfs(nums:List[int],index:int):
nonlocal ans
if index > len(nums) - 1 or ans == True:
return
if index == len(nums) - 1:
ans = True
return
for i in range(nums[index],0,-1):
dfs(nums,index + i)
return
dfs(nums,0)
return ans
贪心:正向查找可到达的最大位置
思想同上跳跃游戏Ⅱ。

class Solution:
def canJump(self, nums: List[int]) -> bool:
ans = True
n = len(nums)
maxPos, end = 0, 0
for i in range(n - 1):
if maxPos >= i:
maxPos = max(maxPos, i + nums[i])
if i == end:
end = maxPos
if maxPos < n - 1:
ans = False
return ans
插入区间*
贪心



class Solution:
def insert(self, intervals: 'List[Interval]', newInterval: 'Interval') -> 'List[Interval]':
# init data
new_start, new_end = newInterval
idx, n = 0, len(intervals)
output = []
# add all intervals starting before newInterval
while idx < n and new_start > intervals[idx][0]:
output.append(intervals[idx])
idx += 1
# add newInterval
# if there is no overlap, just add the interval
if not output or output[-1][1] < new_start:
output.append(newInterval)
# if there is an overlap, merge with the last interval
else:
output[-1][1] = max(output[-1][1], new_end)
# add next intervals, merge with newInterval if needed
while idx < n:
interval = intervals[idx]
start, end = interval
idx += 1
# if there is no overlap, just add an interval
if output[-1][1] < start:
output.append(interval)
# if there is an overlap, merge with the last interval
else:
output[-1][1] = max(output[-1][1], end)
return output

class Solution:
def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]:
i = 0
n = len(intervals)
res = []
# 找左边重合区域
while i < n and newInterval[0] > intervals[i][1]:
res.append(intervals[i])
i += 1
tmp = [newInterval[0], newInterval[1]]
# 找右边重合区域
while i < n and newInterval[1] >= intervals[i][0]:
tmp[0] = min(tmp[0], intervals[i][0])
tmp[1] = max(tmp[1], intervals[i][1])
i += 1
res.append(tmp)
while i < n :
res.append(intervals[i])
i += 1
return res
螺旋矩阵Ⅱ
模拟
算法同上 螺旋矩阵
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
ans = [[0] * n for _ in range(n)]
postions = [[0,1],[1,0],[0,-1],[-1,0]]
posIndex = 0
x , y = 0 , 0
visited = set()
for i in range(n * n):
ans[x][y] = i + 1
visited.add((x,y))
tx = postions[posIndex][0] + x
ty = postions[posIndex][1] + y
if 0 <= tx < n and 0 <= ty < n and (tx,ty) not in visited:
x , y = tx , ty
else:
posIndex = (posIndex + 1) % 4
x , y = x + postions[posIndex][0] , postions[posIndex][1] + y
return ans
第K个排列*
暴力递归(超时)
class Solution:
def getPermutation(self, n: int, k: int) -> str:
if not n:
return ""
def dfs(nList:List[int] , used:List[bool],path:str):
if len(path) == n:
ans.append(path)
return
if len(ans) >= k:
return
for i in range(n):
if used[i] == False:
used[i] = True
dfs(nList,used,path + str(nList[i]))
used[i] = False
pass
nList = []
for i in range(n):
nList.append(i+1)
used = [False] * n
ans = []
dfs(nList,used,"")
return ans[-1]
毫无疑问把暴力递归是可以的,但是这个题的随着n的增加,题目的复杂度是指数级别增长的。哪怕是让递归到题目要求的k时停止,还是会超时。
数学思路


class Solution:
def getPermutation(self, n: int, k: int) -> str:
allFactorial = [1, 1]
for i in range(2, n+1):
allFactorial.append(allFactorial[-1]*i)
s, k, res = list(range(1, n+1)), k-1, ""
for i in range(len(s)-1, -1, -1):
res += str(s[k // allFactorial[i]])
del s[k // allFactorial[i]]
k %= allFactorial[i]
return res
旋转链表
空间换时间
class Solution:
def rotateRight(self, head: ListNode, k: int) -> ListNode:
if not head:
return None
listNode = []
while head!=None:
listNode.append(head)
head = head.next
index , k= (len(listNode) - k) % len(listNode) , len(listNode)
indexNode = listNode[index]
res = indexNode
while k > 1:
k -= 1
index = (index + 1) % len(listNode)
indexNode.next = listNode[index]
indexNode = indexNode.next
indexNode.next = None
return res
不同路径*
暴力递归(超时)
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
res = 0
def dfs(x:int,y:int):
nonlocal res
if x == n - 1 and y == m - 1:
res += 1
for i in range(2):
tx = x + positions[i][0]
ty = y + positions[i][1]
if tx < n and ty < m:
dfs(tx,ty)
positions = [[0,1] , [1,0]]
dfs(0,0)
return res
排列组合

def uniquePaths(self, m: int, n: int) -> int:
return int(math.factorial(m+n-2)/math.factorial(m-1)/math.factorial(n-1))
动态规划

class Solution:
def uniquePaths(self, m: int, n: int) -> int:
dp = [[1]*n] + [[1]+[0] * (n-1) for _ in range(m-1)]
print(dp)
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
动态规划(DP)+滚动数组

class Solution:
def uniquePaths(self, m: int, n: int) -> int:
f = [0 for _ in range(m)]
f[0] = 1
for i in range(0,n):
for j in range(1,m):
f[j] += f[j-1]
return f[m-1]
不同路径Ⅱ
动态规划
该题的答题思路同上题。只是题目中加上了障碍物,就需要去针对障碍物进行处理。
我们令 f[i][j] 是到达i , j 最多路径。动态方程仍然是f[i][j] = f[i - 1][j] + f[i][j - 1]。
在边界时需要注意:
第一行:如果全为0 f中第一行就全为1。如果一旦某点出现一个1,也就是意味着该点之后的所有点在f中都只能为0,因为此路不通。
第一列同理。
在遍历时只需要针对路障处理,只要点i , j为路障,就将其f[i][j]设为0,否则f[i][j] = f[i - 1][j] + f[i][j - 1]。
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
if not obstacleGrid or obstacleGrid[0][0] == 1:
return 0
rows , cols = len(obstacleGrid) , len(obstacleGrid[0])
f = [[0] * cols for _ in range(rows)]
f[0][0] = 1
for j in range(1,cols):
f[0][j] = 1 if obstacleGrid[0][j] == 0 and f[0][j - 1] == 1 else 0
for i in range(1,rows):
f[i][0] = 1 if obstacleGrid[i][0] == 0 and f[i - 1][0] == 1 else 0
for i in range(1,rows):
for j in range(1,cols):
if obstacleGrid[i][j] == 0:
f[i][j] = f[i - 1][j] + f[i][j - 1]
else:
f[i][j] = 0
return f[-1][-1]
最小路径和
动态规划
res[i][j]为到达点(i,j)所走最短路径。
可以贪心的认为保证步步局部最优,最终所得到的即为全局最优。
由于只能向右或向下走,也就是说在点(i,j)时,到达该点的最短路径为到达点(i - 1,j)的最短路径和到达点(i,j - 1)最短路径中最小的那个加上其本身的所需距离,即为到达点(i ,j)的最短路径。
=> res[i][j] = min(res[i - 1][j],res[i][j - 1]) + grid[i][j]
class Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
rows , cols = len(grid) , len(grid[0])
res = [[0] * cols for _ in range(rows)]
res[0][0] = grid[0][0]
for j in range(1,cols):
res[0][j] = res[0][j - 1] + grid[0][j]
for i in range(1,rows):
res[i][0] = res[i - 1][0] + grid[i][0]
for i in range(1,rows):
for j in range(1,cols):
res[i][j] = min(res[i - 1][j],res[i][j - 1]) + grid[i][j]
return res[-1][-1]
有效数字
正则匹配
能够成功转成十进制的有”.1” “1.” “-01” “.1e3” “2.e3” “2e-3”等。即写出对应的正则表达式即可。
class Solution:
def isNumber(self, s: str) -> bool:
int()
s = s.strip()
if len(s) == 0:
return False
p_ne = re.compile(r'(^((\+|\-)?\d+\.{1}\d*)|((\+|\-)?\d*\.{1}\d+)|((\+|\-)?\d+)$)',re.I)
p_e = re.compile(r'(^((\+|\-)?\d+\.{1}\d*e{1}(\+|\-)?\d+)|((\+|\-)?\d*\.{1}\d+e{1}(\+|\-)?\d+)|((\+|\-)?\d+e{1}(\+|\-)?\d+)$)',re.I)
result = None
if 'e' in s :
result = re.search(p_e,s)
else:
result = re.search(p_ne,s)
if result != None:
# print(result.group())
if len(result.group()) == len(s):
return True
return False
别人家的正则
class Solution:
def isNumber(self, s: str) -> bool:
return bool(re.match(r'\s*[+-]?([\d]+(\.[\d]*)?|\.[\d]+)(e[+-]?[\d]+)? *$', s))
# [\d]+(\.[\d]*)? 是一体的
或者是:
[+-]?((\d+(\.\d*)?)|\.\d+)([e][+-]?\d+)?
文本左右对齐
按照题意 硬解 (空间复杂度高)
就是先根据单词大小分组,然后再根据每行的单词数量进行计算所需空格数量,然后再根据题目要求进行将单词和空格合并。
class Solution:
def fullJustify(self, words: List[str], maxWidth: int) -> List[str]:
res = []
wordLenList = []
wordLen = 0
rowsList = []
for val in words:
if wordLen + len(val) + len(rowsList) <= maxWidth:
rowsList.append(val)
wordLen += len(val)
else:
res.append(rowsList)
wordLenList.append(wordLen)
rowsList = [val]
wordLen = len(val)
if len(rowsList) != 0:
res.append(rowsList)
wordLenList.append(wordLen)
for i in range(len(res)):
row = ""
sList = [0] * (len(res[i]) - 1)
index = 0
# 计算所需空格数
if len(res[i]) == 1:
sList.append(maxWidth - wordLenList[i])
else:
sNum = maxWidth - wordLenList[i] # 所需空格总数
while sNum > 0:
sList[index] = 1 + sList[index]
index = (index + 1) % (len(res[i]) - 1)
sNum -= 1
if i == len(res) - 1: # 最后一行空格单独处理
sList = []
for _ in range(len(res[i]) - 1):
sList.append(1)
sList.append(maxWidth - wordLenList[i] - (len(res[i]) - 1))
# print(sList)
for j in range(len(res[i])):
if j == len(res[i]) - 1:
if i != len(res) - 1 and len(res[i]) != 1:
row += res[i][j]
else:
row += res[i][j] + " " * sList[j]
else:
row += res[i][j] + " " * sList[j]
res[i] = row
return res
爬楼梯*
直接递归(超时)
class Solution:
def climbStairs(self, n: int) -> int:
res = 0
def dfs(n:int,path:int):
nonlocal res
if path == n:
res += 1
return
if path > n:
return
for i in range(1,3):
dfs(n,path+i)
return
dfs(n,0)
return res
# 直接递归解法,容易超时,python可以加个缓存装饰器,这样也算是将递归转换成迭代的形式了
# 除了这种方式,还有增加步长来递归,变相的减少了重复计算
# 还有一种方法,在递归的同时,用数组记忆之前得到的结果,也是减少重复计算
class Solution:
@functools.lru_cache(100) # 缓存装饰器
def climbStairs(self, n: int) -> int:
if n == 1: return 1
if n == 2: return 2
return self.climbStairs(n-1) + self.climbStairs(n-2)
动态规划



class Solution:
def climbStairs(self, n: int) -> int:
if n <=2:
return n
dp = [0]*n
dp[0] = 1
dp[1] = 2
for i in range(2,n):
dp[i] = dp[i-1]+dp[i-2]
return dp[n-1]
滚动数组
class Solution:
def climbStairs(self, n: int) -> int:
if n <= 2:
return n
a = 1
b = 2
for i in range(3,n+1):
t = b
b = a + b
a = t
return b
简化路径
正则+栈
import re
class Solution:
def simplifyPath(self, path: str) -> str:
path = re.sub(r'/+','/',path) # 替换连续的/
if len(path) > 1 and path[-1] == "/": # 去除最后一个/
path = path[0:-1]
strList = path.split("/")
stack = []
for val in strList:
if val == ".":
continue
elif val == ".." :
if len(stack) > 1:
stack.pop()
else:
stack.append(val)
res = "/".join(stack) if len(stack) > 1 else "/"
return res
编辑距离*
动态规划
























class Solution:
def minDistance(self, word1, word2):
"""
:type word1: str
:type word2: str
:rtype: int
"""
n = len(word1)
m = len(word2)
# 有一个字符串为空串
if n * m == 0:
return n + m
# DP 数组
D = [ [0] * (m + 1) for _ in range(n + 1)]
# 边界状态初始化
for i in range(n + 1):
D[i][0] = i
for j in range(m + 1):
D[0][j] = j
# 计算所有 DP 值
for i in range(1, n + 1):
for j in range(1, m + 1):
left = D[i - 1][j] + 1
down = D[i][j - 1] + 1
left_down = D[i - 1][j - 1]
if word1[i - 1] != word2[j - 1]:
left_down += 1
D[i][j] = min(left, down, left_down)
return D[n][m]
矩阵置零
O(mn)的空间复杂度
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
rows , cols = len(matrix) , len(matrix[0])
mStr = ",".join(str(matrix[i][j]) for i in range(rows) for j in range(cols)).split(",")
# print(mStr)
for index in range(len(mStr)):
if mStr[index] == "0":
i , j = index // cols , index % cols
print(i,j)
for mi in range(rows):
matrix[mi][j] = 0
for mj in range(cols):
matrix[i][mj] = 0
# print(matrix)
pass
O(1)空间复杂度


class Solution(object):
def setZeroes(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: void Do not return anything, modify matrix in-place instead.
"""
is_col = False
R = len(matrix)
C = len(matrix[0])
for i in range(R):
# Since first cell for both first row and first column is the same i.e. matrix[0][0]
# We can use an additional variable for either the first row/column.
# For this solution we are using an additional variable for the first column
# and using matrix[0][0] for the first row.
if matrix[i][0] == 0:
is_col = True
for j in range(1, C):
# If an element is zero, we set the first element of the corresponding row and column to 0
if matrix[i][j] == 0:
matrix[0][j] = 0
matrix[i][0] = 0
# Iterate over the array once again and using the first row and first column, update the elements.
for i in range(1, R):
for j in range(1, C):
if not matrix[i][0] or not matrix[0][j]:
matrix[i][j] = 0
# See if the first row needs to be set to zero as well
if matrix[0][0] == 0:
for j in range(C):
matrix[0][j] = 0
# See if the first column needs to be set to zero as well
if is_col:
for i in range(R):
matrix[i][0] = 0
搜索二维矩阵
二分查找
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
if not matrix:
return False
rows , cols = len(matrix) , len(matrix[0])
sumLen = rows * cols
left , right = 0 , sumLen - 1
while left <= right:
mid = (left + right) // 2
i , j = mid // cols , mid % cols
if matrix[i][j] == target:
return True
elif matrix[i][j] < target:
left = mid + 1
else:
right = mid - 1
return False
颜色分类
单指针

class Solution:
def sortColors(self, nums: List[int]) -> None:
n = len(nums)
ptr = 0
for i in range(n):
if nums[i] == 0:
nums[i], nums[ptr] = nums[ptr], nums[i]
ptr += 1
for i in range(ptr, n):
if nums[i] == 1:
nums[i], nums[ptr] = nums[ptr], nums[i]
ptr += 1
双指针


















class Solution:
def sortColors(self, nums: List[int]) -> None:
n = len(nums)
p0 = p1 = 0
for i in range(n):
if nums[i] == 1:
nums[i], nums[p1] = nums[p1], nums[i]
p1 += 1
elif nums[i] == 0:
nums[i], nums[p0] = nums[p0], nums[i]
if p0 < p1:
nums[i], nums[p1] = nums[p1], nums[i]
p0 += 1
p1 += 1
最小覆盖子串
滑动窗口



def minWindow(self, s: str, t: str) -> str:
need=collections.defaultdict(int)
for c in t:
need[c]+=1
needCnt=len(t)
i=0
res=(0,float('inf'))
for j,c in enumerate(s):
if need[c]>0:
needCnt-=1
need[c]-=1
if needCnt==0: #步骤一:滑动窗口包含了所有T元素
while True: #步骤二:增加i,排除多余元素
c=s[i]
if need[c]==0:
break
need[c]+=1
i+=1
if j-i<res[1]-res[0]: #记录结果
res=(i,j)
need[s[i]]+=1 #步骤三:i增加一个位置,寻找新的满足条件滑动窗口
needCnt+=1
i+=1
return '' if res[1]>len(s) else s[res[0]:res[1]+1] #如果res始终没被更新过,代表无满足条件的结果
组合
递归
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
def dfs(path:List[int]):
if len(path) == k:
res.append(path)
return
for index in range(n):
if len(path) == 0 or(len(path) > 0 and path[-1] < index + 1):
dfs(path+[index+1])
return
dfs([])
return res
迭代器 - itertools函数
https://docs.python.org/zh-cn/3/library/itertools.html
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
return list(itertools.combinations(range(1,n+1),k))
子集
迭代器 - itertools函数
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
res = [[]]
for n in range(1,len(nums) + 1):
for l in list(itertools.combinations(nums,n)):
res.append(list(l))
return res
单词搜索
DFS 回溯
class Solution:
def exist(self, board: List[List[str]], word: str) -> bool:
m = len(board)
n = len(board[0])
visited = [[False] * n for _ in range(m)]
rows = [-1, 0, 1, 0]
cols = [0, 1, 0, -1]
def dfs(x, y, idx):
if board[x][y] != word[idx]:
return False
if idx == len(word) - 1:
return True
# 先标记
visited[x][y] = True
# 找到符合的字母时开始向四个方向扩散搜索
for i in range(4):
nx = x + rows[i]
ny = y + cols[i]
if 0 <= nx < m and 0 <= ny < n and not visited[nx][ny] and dfs(nx, ny, idx+1):
return True
# 扩散未搜索对应的字母,释放标记
# 继续往其他方位搜索
visited[x][y] = False
return False
for x in range(m):
for y in range(n):
if dfs(x, y, 0):
return True
return False













