数据规模->时间复杂度
<=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























