算法秘籍之拿下回溯

回溯算法也称暴力解算法,这类问题分为几大类别,快速识别不同的回溯方法可以极大的帮助我们处理回溯问题,下面是根据本人最近刷的力扣回溯专题做的一个总结。

  • 常规回溯
  • 元素可重复选
  • 元素不可重复选
  • 排列问题(考虑顺序)
  • 组合问题(不考虑顺序)
  • 解存在问题

常规回溯
回溯算法也就是多叉树的深度遍历,有多少个选择就有多少层,下面是回溯算法的模板结构。

 def trace_back(与结束条件有关的参数(一般是层数,元素个数,字符串长度,可以无->则遍历路径上的所有值可作为解), 初始的求解参数(如果存在多个可以外部套for遍历初始值)):
     if 结束条件(也叫剪纸条件):
       收集结果(收集数组或字符串结果,收集bool结果可以 if trace_back():然后深度判断是否存在True)
     for _ in 可选择列表(结果树的分支数):
       trace_back()
       #撤销操作,撤销当前选择的元素
       de_trace_back

元素可重复
元素可重复选择:可以使用for循环每次回溯都可以选择所有元素
元素不可重复
元素不可重复选择:可以使用dict标记已经选过的元素,for循环中通过判断元素是否使用来决定回溯

# 初始每个元素都没有被选择所以都为False
uselist = [False]*n
def trace_back(n):
	if 结束条件:
		收集结果
	for x in 可选的元素列表:
		# 如果元素没有被选过则继续遍历
		if not uselist[x]:
			trace_back(x)
			de_trace_back()

排列问题
考虑顺序的选择:不同的选择顺序可以作为一个解,解法与不可重复类似确保每个元素都被选上且仅选择一次。
排列问题

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        length = len(nums)
        uselist = [False] * length
        ans = []
        tem = []
        def back_tracing(n):
            if n == length:
                ans.append(tem[:])

            for i in range(length):
                if not uselist[i]:
                    uselist[i] = True
                    tem.append(nums[i])
                    back_tracing(n + 1)
                    uselist[i] = False
                    tem.pop()
        back_tracing(0)
        return ans

组合问题
不考虑顺序的选择:相同元素不同顺序不作为新解,此时需要有回溯的方向,不可以走回头路。(列表的体现就是下标一直为增直到最后一个元素),例如下题中的start_index控制回溯方向,每次回溯做出选择后持续向后一个元素做选择。
组合问题1

 class Solution:
     def subsetXORSum(self, nums: List[int]) -> int:
         def trace_back(item, start_index):
             if start_index == len(nums):
                 ans.append(item)
                 return
             trace_back(item^nums[start_index], start_index + 1)
             trace_back(item, start_index + 1)
         ans = []
         trace_back(0, 0)
         print(ans)
         return sum(ans)

组合问题2

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        tem = []


        def trace_back(cur, n, k):
            if n - cur + 1 < k:
                return
            if k == 0:
                ls = copy.deepcopy(tem)
                ans.append(ls)
                return 
            
            tem.append(cur)
            trace_back(cur+1, n, k-1)
            tem.pop()
            trace_back(cur+1, n, k)
        trace_back(1, n, k)
        return ans

解存在问题
只收集一个结果或者判断是否存在问题,则收集bool结果
例如:给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。
如果 word 存在于网格中,返回 true ;否则,返回 false

 def exist(self, board: List[List[str]], word: str) -> bool:
         visited = {}
         rows = len(board)
         colums = len(board[0])
         n = len(word)
         def find_word(row, colum, k):
             if board[row][colum] != word[k]:
                 return False
             if k == n - 1:
                 return True
             result = False
             visited[(row, colum)] = True
             for (r, c) in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
                 if 0 <= row + r < rows and 0 <= colum + c < colums:
                     if not visited.get((row + r, colum + c), False):
                         这里只要判断存在一个解立马退出后序遍历,返回结果,只需要判断是否存在解
                         if find_word(row + r, colum + c, k + 1):
                             result = True
                             break
             visited[(row, colum)] = False
             return result
         for i in range(rows):
             for j in range(colums):
                 if find_word(i, j, 0):
                     return True
         return False

所以解决回溯问题的算法步骤分为如下:

  1. 找到求解结束条件
  2. 判断选择方法,是否是可重复选择
  3. 找到起始求解值
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值