BackTracking

回溯----递归

解决的问题

1. 组合问题 

2. 切割问题

3. 子集问题

4. 排列问题

5. 棋盘问题

void backtracking(){
    if(终止条件){
        收集结果
        return;
    }
    for(集合元素){
        处理结点
        递归
        回溯(撤销之前的)
    }
    return;
}

组合问题 

i+1

i

i+1 & used

77. Combinations

tip:

path[:] copy一个path数组,再append进res,这样之后path的改变不会引起res的改变

class Solution:
    def __init__(self):
        self.path = []
        self.res = []
    def combine(self, n: int, k: int) -> List[List[int]]:
        self.backtracking(n, k, 1)
        return self.res
        
    def backtracking(self, n, k, startIndex):
        # 1. 递归函数的参数和返回值
        # 2. 确定终止条件
        if len(self.path) == k:
            # 收集结果
            self.res.append(self.path[:])

            return
        
        # 3. 单层递归逻辑
        for i in range(startIndex, n+1):
            self.path.append(i)
            self.backtracking(n, k, i+1)
            self.path.pop() # 回溯

216. Combination Sum III

递归终止条件加1

17. Letter Combinations of a Phone Number

递归:每一层换一个digit-----找到下一层递归的digit

for的每层: 遍历一个digit

class Solution:
    def __init__(self):
        self.code_map =[ '',
                        '', 
                        'abc',  # 2
                        'def',
                        'ghi', 
                        'jkl', 
                        'mno',
                        'pqrs',
                        'tuv',
                        'wxyz' # 9
                        ]
        self.path = ''
        self.res = []

    def letterCombinations(self, digits: str) -> List[str]:
        if len(digits) == 0:
            return []
        self.backtracking(digits, 0)
        return self.res


    def backtracking(self, digits, index):

        if len(self.path) == len(digits):
            self.res.append(self.path)
            return
        
        digit = int(digits[index])
        code_digit = self.code_map[digit] #'abc'
        for i in range(len(code_digit)):
            self.path += code_digit[i]
            self.backtracking(digits, index+1)
            self.path = self.path[:-1]

39. Combination Sum

tip

1. 组合问题-----【2,3】和【3,2】没有区别------下一层递归从i开始

2. 可以元素重复----下一层递归可以和上一层用一个元素---从i开始

递归终止条件是sum,pop的时候应该将sum减去pop的元素

class Solution:
    def __init__(self):
        self.path = []
        self.res = []

    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        # 组合:2,3 = 3,2
        # 可以重复使用
        self.backtracking(candidates, target, 0,0)
        return self.res
        
    def backtracking(self, candidates, target, index,sum_all):
        n = len(candidates)
        if sum_all == target:
            self.res.append(self.path[:])
            return
        if sum_all > target:
            return
        for i in range(index, n):
            self.path.append(candidates[i])
            sum_all += candidates[i]
            self.backtracking(candidates, target, i,sum_all)
            sum_all -= candidates[i]
            self.path.pop()

40. Combination Sum II

tip

组合问题

每个元素只能用一次,但是给出的元素有重复的

会导致最后结果有重复:[1,7] 和 [7,1] 用的虽然不是同一个1,但是结果重复了

candidates[i] == candidates[i-1]: 去掉【1*,7】, 【1**,7】的重复

used[i-1] == 0: 防止把【1*,1**,7】的也去掉了

class Solution:
    def __init__(self):
        self.path = []
        self.res = []

    def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
        # 关键----去重----树层去重
        candidates.sort()
        used = [0] * len(candidates)
        self.backtracking(candidates, target, 0,0,used)
        return self.res

    def backtracking(self, candidates, target, index, sum_all, used):
        if sum_all == target:
            self.res.append(self.path[:])
            return
        if sum_all > target:
            return
        
        for i in range(index, len(candidates)):
            if i > 0 and candidates[i] == candidates[i-1] and used[i-1] == 0:
                # 防止去掉 上下两层,第一层取第一个1,第二层取第二个1 的i情况
                continue
            self.path.append(candidates[i])
            used[i] = 1
            sum_all += candidates[i]
            self.backtracking(candidates, target, i+1, sum_all, used )
            sum_all -= candidates[i]
            used[i] = 0
            self.path.pop()

分割问题

131. Palindrome Partitioning

tip

找substring----分割

class Solution:
    def __init__(self):
        self.path = []
        self.res = []

    def partition(self, s: str) -> List[List[str]]:
        self.backtracking(s, 0)
        return self.res
        
    def backtracking(self, s, cutIndex ):
        # 切割结束才收集path
        if cutIndex == len(s):
            self.res.append(self.path[:])
        
        for i in range(cutIndex, len(s)):
            cut = s[cutIndex : i+1]
            if self.isPal(cut) == True:
                self.path.append(cut)
            else:
                continue
            self.backtracking(s, i+1)
            self.path.pop()

    def isPal(self, s:str):
        reverse_s = s[::-1]
        return s == reverse_s

子集问题

i+1 & 全部收集

78. Subsets

class Solution:
    def __init__(self):
        self.path = []
        self.res = []
    def subsets(self, nums: List[int]) -> List[List[int]]:
        self.backtracking(nums, 0)
        return self.res
        
    def backtracking(self, nums, index):
        # 收集结果的时候 每个节点都要收集
        self.res.append(self.path[:])
        # 但是只有到叶子节点的时候才进入下一层递归
        if index >= len(nums):
            return
        for i in range(index, len(nums)):
            self.path.append(nums[i])
            self.backtracking(nums, i + 1)
            self.path.pop()

90. Subsets II

class Solution:
    # 去重
    def __init__(self):
        self.path = []
        self.res = []

    def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        used = [0] * len(nums)
        self.backtracking(nums,0, used)
        return self.res
        
    def backtracking(self, nums, index, used):
        self.res.append(self.path[:])
        if index >= len(nums):
            return 
        for i in range(index, len(nums)):
            if i > 0 and nums[i] == nums[i-1] and used[i-1] == 0:
                continue
            self.path.append(nums[i])
            used[i] = 1
            self.backtracking(nums, i+1,used)
            self.path.pop()    
            used[i] = 0 

所谓return就是进入下一层树形结构(进入下一层递归)

所以是只有for循环的index走完本层的时候还会return, 但是每一个节点都要收集到

需要收集每个节点的时候---先收集,再判断终止条件return

491. Non-decreasing Subsequences

不能用sort因为本身数组的顺序不可以改变了

class Solution:
    def __init__(self):
      self.path = []
      self.res = []
      
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
      self.backtracking(nums,0)       
      return self.res
    
    def backtracking(self, nums, index):
      if len(self.path) >= 2:
          self.res.append(self.path[:])
      if index >= len(nums):
          return 
      uset = set()
      for i in range(index, len(nums)):
          # 当前元素在本层出现过
          if (self.path and nums[i] < self.path[-1]) or nums[i] in uset :
              continue
          uset.add(nums[i])
          self.path.append(nums[i])
          self.backtracking(nums, i+1)
          self.path.pop()  
          


        

排列 

path去重

path去重 & used

46. Permutations

记录当前路径是否用过----同一路径不可以重复 

class Solution:
    def __init__(self):
        self.path = []
        self.res = []

    def permute(self, nums: List[int]) -> List[List[int]]:
        used = [0] * len(nums)
        self.backtracking(nums,used)
        return self.res

    def backtracking(self, nums, used):
        if len(self.path) == len(nums):
            self.res.append(self.path[:])
            return
        for i in range(len(nums)):
            if used[i]:
                continue
            self.path.append(nums[i])
            used[i] = True
            self.backtracking(nums, used)
            self.path.pop()
            used[i] = False

47. Permutations II

class Solution:
    def __init__(self):
        self.path = []
        self.res = []

    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        # 去重
        nums.sort()
        used = [0] * len(nums)
        self.backtracking(nums,used)
        return self.res
    
    def backtracking(self, nums, used):
        if len(self.path) == len(nums):
            self.res.append(self.path[:])
            return
        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i-1] and used[i-1] == 0:
                continue
            if used[i]:
                continue
            
            self.path.append(nums[i])
            used[i] = 1
            self.backtracking(nums,used)
            self.path.pop()    
            used[i] = 0 
        

棋盘问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值