数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
内容
lc 112 和 113【剑指 34】 :路径总和
https://leetcode.cn/problems/path-sum/
https://leetcode.cn/problems/path-sum-ii/
提示1:
树中节点的数目在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
提示2:
树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
#lc 112
#方案一:穷举所有路径+判断是否存在路径和(空间复杂度高)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
self.res=list()
path=list()
self.dfs(root,path)
#
for onepath in self.res:
if targetSum==sum(onepath):
return True
return False
def dfs(self,node,path):
if not node:return
path.append(node.val)
if not node.left and not node.right: #叶子节点
self.res.append(path[:]) #key:path[:],使res和path不能是同一个对象(‘回溯时清空’)
#
self.dfs(node.left,path)
self.dfs(node.right,path)
path.pop() #注:回溯时删当前节点
#方案二:计算每个节点的路径和(优化空间)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
self.res=list()
self.dfs(root,0)
#
for val in self.res:
if targetSum==val:
return True
return False
def dfs(self,node,onepathsum):
if not node:return
onepathsum +=node.val
if not node.left and not node.right: #叶子节点
self.res.append(onepathsum)
#
self.dfs(node.left,onepathsum)
self.dfs(node.right,onepathsum)
##方案三:计算每个节点的目标和(优化空间)
# Definition for a binary tree node.
# # class TreeNode:
# # def __init__(self, val=0, left=None, right=None):
# # self.val = val
# # self.left = left
# # self.right = right
# class Solution:
# def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
# self.res=list()
# self.dfs(root,targetSum)
# #
# for val in self.res:
# if val==0:
# return True
# return False
# def dfs(self,node,onepathtarget):
# if not node:return
# onepathtarget -=node.val
# if not node.left and not node.right: #叶子节点
# self.res.append(onepathtarget)
# #
# self.dfs(node.left,onepathtarget)
# self.dfs(node.right,onepathtarget)
#方案四:提前返回(优化时间)
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
return self.dfs(root,targetSum)
def dfs(self,node,onepathtarget): #bool
if not node:return False #key:false
onepathtarget -=node.val
if not node.left and not node.right: #叶子节点
return onepathtarget==0
#
islefthasfind=self.dfs(node.left,onepathtarget)
if islefthasfind:return True #提前退出
return self.dfs(node.left,onepathtarget) or self.dfs(node.right,onepathtarget)
#lc 113
#方案一:拿到符合条件的路径及优化
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
self.res=list()
path=list()
self.dfs(root,path,targetSum)
#
return self.res
def dfs(self,node,path,targetSum):
if not node:return
path.append(node.val)
if not node.left and not node.right:
if sum(path)==targetSum:
self.res.append(path[:])
#
self.dfs(node.left,path,targetSum)
self.dfs(node.right,path,targetSum)
path.pop()
lc 46 和 47【剑指 083 和 084】【top100】 :全排列
https://leetcode.cn/problems/permutations/
https://leetcode.cn/problems/permutations-ii/
提示1:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
提示2:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
nums 中的整数存在重复
#参考:https://www.bilibili.com/video/BV19v4y1S79W/?spm_id_from=333.337.search-card.all.click&vd_source=faf49b32c764ca86336e7f3fdff2c71f
#lc 46
#时间:o(n!* n)
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
self.res=list()
path=list()
used=[False]*len(nums)
self.dfs(nums,path,used)
return self.res
#o(n)
def dfs(self,nums,path,used):
if len(path)==len(nums):#key:两层含义-终止;叶子节点
self.res.append(path[:])
return
#
for i in range(len(nums)):
#
if used[i]:continue
path.append(nums[i])
used[i]=True
#
self.dfs(nums,path,used)
#key:在回溯过程中,初始化(例如:1,2,3;1,3,2)
path.pop()
used[i]=False
#lc 47
#结果去重剪枝
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums.sort()#key1:去重基础
self.res=list()
path=list()
used=[False]*len(nums)
self.dfs(nums,path,used)
return self.res
def dfs(self,nums,path,used):
#
if len(nums)==len(path):
self.res.append(path[:])
return
#
for i in range(len(nums)):
#
if used[i]:continue
#这里的used[i-1]已经访问过,并回溯初始化了,为False
#参考:https://gitee.com/douma_edu/douma_algo_training_camp/issues/I48M6Q
if i>0 and nums[i]==nums[i-1] and not used[i-1]:continue #key2:树层去重
path.append(nums[i])
used[i]=True
#
self.dfs(nums,path,used)
path.pop()
used[i]=False
lc 77【剑指 080】:组合
https://leetcode.cn/problems/combinations/
提示:
1 <= n <= 20
1 <= k <= n
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
if n<0 or k<0 or k>n:return []
self.res=list()
path=[]
self.dfs(n,k,1,path)
return self.res
def dfs(self,n,k,start,path):
if len(path)==k:
self.res.append(path[:])
return
#key:start,i+1
for i in range(start,n+1):
path.append(i)
self.dfs(n,k,i+1,path)
path.pop()
lc 39 和 40【剑指 081 和 082】【top100】:组合总和
https://leetcode.cn/problems/combination-sum/
https://leetcode.cn/problems/combination-sum-ii/
提示1:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40
candidates中的每个数字可重复使用
提示2:
1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30
candidates中的每个数字只能使用一次
#lc 39
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
self.res=[]
path=list()
self.dfs(candidates,0,path,target)
return self.res
def dfs(self,nums,startindex,path,target):
if target<0:return
if target==0:
self.res.append(path[:])
return
#
for i in range(startindex,len(nums)):
path.append(nums[i])
#target-=nums[i]
#key
self.dfs(nums,i,path,target-nums[i])
path.pop()
#target+=nums[i]
#lc 40
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
candidates.sort() #key1
self.res=[]
path=[]
self.dfs(candidates,0,path,target)
return self.res
def dfs(self,nums,startindex,path,target):
if target<0:return
if target==0:
self.res.append(path[:])
return
#
for i in range(startindex,len(nums)):
#key2:去重关键(树层去重)
if i>startindex and nums[i]==nums[i-1]:continue#key:i>startindex
path.append(nums[i])
self.dfs(nums,i+1,path,target-nums[i])#key:i+1,非重复选取索引(树枝去重)
path.pop()
lc 78 和 90【剑指 079】【top100】:子集
https://leetcode.cn/problems/subsets/
https://leetcode.cn/problems/subsets-ii/
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
第一种,#nums 中的所有元素 互不相同#
第二种,#nums 中的所有元素含重复元素#
# lc 78
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
self.res=[]
path=list()
self.dfs(nums,0,path)
return self.res
def dfs(self,nums,startindex,path):
#key:‘子集’
self.res.append(path[:])
for i in range(startindex,len(nums)):
path.append(nums[i])
self.dfs(nums,i+1,path)
path.pop()
#lc 90
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
#key1
nums.sort()
self.res=[]
path=list()
self.dfs(nums,0,path)
return self.res
def dfs(self,nums,startindex,path):
#key:‘子集’
self.res.append(path[:])
for i in range(startindex,len(nums)):
if i>startindex and nums[i]==nums[i-1]:continue #key2
path.append(nums[i])
self.dfs(nums,i+1,path)
path.pop()
lc 17【top100】:电话号码的字母组合
https://leetcode.cn/problems/letter-combinations-of-a-phone-number/
提示:
0 <= digits.length <= 4
digits[i] 是范围 [‘2’, ‘9’] 的一个数字。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:return []
self.phone = {
"2": "abc",
"3": "def",
"4": "ghi",
"5": "jkl",
"6": "mno",
"7": "pqrs",
"8": "tuv",
"9": "wxyz",
}
self.res=[]
path=''
self.dfs(digits,0,path)
return self.res
def dfs(self,digits,index,path):
if index==len(digits):
self.res.append(path[:])
return
#key
for c in self.phone[digits[index]]:
self.dfs(digits,index+1,path+c)
lc 93【剑指 087】:复原 IP 地址
https://leetcode.cn/problems/restore-ip-addresses/
提示:
1 <= s.length <= 20
s 仅由数字组成
class Solution:
def restoreIpAddresses(self, s: str) -> List[str]:
self.res=[]
path=''
self.dfs(s,0,path,0)
return self.res
def is_valid_seg(self,seg):
if len(seg)>3:return False
return len(seg)==1 if seg[0]== '0' else int(seg)<=255
def dfs(self,s,startindex,path,seg_cnts):
if seg_cnts>4:return
if seg_cnts==4 and startindex==len(s):
self.res.append(path) #[:]
return
#key
for i in range(1,4):
if startindex+i>len(s):break
#
seg=s[startindex:startindex+i] #取不到startindex+i
if not self.is_valid_seg(seg):continue
suffix='' if seg_cnts==3 else '.' #seg_cnts+1=4
self.dfs(s,startindex+i,path+seg+suffix,seg_cnts+1)
lc 22【剑指 085】【top100】:括号生成
https://leetcode.cn/problems/generate-parentheses/
提示:
1 <= n <= 8
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
self.res=[]
self.dfs(n,'',0,0)
return self.res
def dfs(self,n,path,open,close):
if open>n or close>open:return
if len(path)==2*n:
self.res.append(path)
return
#
self.dfs(n,path+'(',open+1,close)
self.dfs(n,path+')',open,close+1)
lc 51 :N 皇后
https://leetcode.cn/problems/n-queens/
提示:
1 <= n <= 9
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
self.res=[]
#
self.rows=[0]*n #标-行攻位
self.cols=[0]*n #列攻位
self.mains=[0]*(2*n-1) #主对角
self.secondary=[0]*(2*n-1) #副对角
#
self.dfs(0,n)
return self.res
def dfs(self,row,n):
if row>=n:return
#
for col in range(n):
if self.is_valid_pos(row,col,n):
self.placeQ(row,col,n)
if row==n-1:self.addsolution(n)
#下一行放置
self.dfs(row+1,n)
self.removeQ(row,col,n)#撤销,回溯
def is_valid_pos(self,row,col,n):
#+self.rows[row],可省略,因为是一行一行放的,判断时候行中是肯定无的
return self.cols[col]+self.mains[row-col+(n-1)]+self.secondary[row+col]==0
def placeQ(self,row,col,n):
self.rows[row]=col
self.cols[col]=1
self.mains[row-col+(n-1)]=1
self.secondary[row+col]=1
def addsolution(self,n):
sol=[]
for i in range(n):
#每一行结果
col=self.rows[i]
sb=''
for j in range(col):sb+='.'
sb+='Q'
for j in range((n-col)-1):sb+='.' #例如:col=1,(,Q,,)n=4
#
sol.append(sb)
self.res.append(sol)
def removeQ(self,row,col,n):
self.rows[row]=0
self.cols[col]=0
self.mains[row-col+(n-1)]=0
self.secondary[row+col]=
lc 37 :数独问题
https://leetcode.cn/problems/sudoku-solver/
提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 ‘.’
题目数据 保证 输入数独仅有一个解
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
#初始化
self.row_used=[[False]*10 for _ in range(9)] #[9][10]
self.col_used=[[False]*10 for _ in range(9)] #[9][10]
self.box_used=[[[False]*10 for _ in range(3)] for _ in range(3)] #[3][3][10]
for i in range(len(board)):
for j in range(len(board[0])):
num=ord(board[i][j])-ord('0')
if 1<=num<=9:
self.row_used[i][num]=self.col_used[j][num]=self.box_used[i//3][j//3][num]=True
#
self.dfs(board,0,0)
def dfs(self,board,row,col):
if col==len(board[0]):
row+=1
col=0
if row==len(board):
return True
if board[row][col]=='.': #填充条件
for num in range(1,10):
can_place= self.row_used[row][num]==self.col_used[col][num]==self.box_used[row//3][col//3][num]==False #key
if can_place:
self.row_used[row][num]=self.col_used[col][num]=self.box_used[row//3][col//3][num]=True
board[row][col]=str(num)
##填充成功,返回true
if self.dfs(board,row,col+1):return True
##填充失败,回溯,撤销
self.row_used[row][num]=self.col_used[col][num]=self.box_used[row//3][col//3][num]=False
board[row][col]='.'
else: #跳格
return self.dfs(board,row,col+1)
return False