python 回溯算法

  • 回溯算法理论基础
  • 组合
  • 组合总数III
  • 电话号码的字母组合
  • 组合总和
  • 组合总和ii
  • 分割回文串
  • 复原IP地址
  • 子集问题
  • 子集问题II
  • 递增序列
  • 全排列
  • 全排列II
  • 重新安排行程
  • N皇后
  • 解数独

回溯算法理论基础

回溯算法解决的问题都可以抽象为树形结构(N叉树),用树形结构来理解回溯会容易很多。

回溯法一般可以解决如下几种问题:

  • 组合问题:N个数里面按一定规则找出K个数的集合
  • 切割问题:一个字符串按一定的规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等

回溯算法模板:

  • 回溯算法的返回值和参数
    回溯算法中返回值一般为空,参数更具后续的逻辑来,先写逻辑,再写参数,需要什么参数就填什么参数

  • 回溯终止条件:存放结果

  • 回溯的遍历过程:处理节点, for(调用自己, 回溯(递归),撤销处理结果)。for为横向遍历,递归为纵向遍历

组合

# 不剪枝
class Solution:def combine(self, n: int, k: int) -> List[List[int]]:self.res = []  # 存放符合条件结果的集合self.combine = []  # 用来存放符合条件结果self.backtrack(n, k , 1)return self.resdef backtrack(self, n, k, start_index):if len(self.combine) == k:# 这里要append的是self.combine里面的值而不是self.combine,因为self.combine在循环里被pop()最后都是空的self.res.append(self.combine[:])returnfor i in range(start_index, n + 1):self.combine.append(i)  # 处理节点self.backtrack(n, k , i + 1)  # 递归self.combine.pop()  # 回溯,撤销处理的节点
# 剪枝
class Solution:def combine(self, n: int, k: int) -> List[List[int]]:self.res = []self.combine = []self.backtrack(n, k , 1)return self.resdef backtrack(self, n, k, start_index):if len(self.combine) == k:# 这里要append的是self.combine里面的值而不是self.combine,因为self.combine在循环里被pop()最后都是空的self.res.append(self.combine[:])return for i in range(start_index, n - (k - len(self.combine)) + 2):  # 剪枝self.combine.append(i)self.backtrack(n, k , i + 1)self.combine.pop()

组合总数III

class Solution:def combinationSum3(self, k: int, n: int) -> List[List[int]]:self.res = []self.combine = []self.backtrack(n, k, 1)return self.resdef backtrack(self, n, k, start_index):if sum(self.combine) > n:return# 这里把计算sum放前面可能会超时,因为每个都要计算,所以先计算长度if len(self.combine) == k and sum(self.combine) == n:self.res.append(self.combine[:])returnfor i in range(start_index, 9 - (k - len(self.combine)) + 1 + 1):self.combine.append(i)self.backtrack(n, k, i + 1)self.combine.pop()

电话号码的字母组合

class Solution:def letterCombinations(self, digits: str) -> List[str]:if not digits: return []self.res = []self.answer = ''self.number = list(digits)self.map = {'2': 'abc','3': 'def','4': 'ghi','5': 'jkl','6': 'mno','7': 'pqrs','8': 'tuv','9': 'wxyz'}self.backtrack(0)return self.resdef backtrack(self, index):if index == len(self.number):self.res.append(self.answer[:])returnletters = self.map[self.number[index]]for letter in letters:self.answer += letterself.backtrack(index + 1)self.answer = self.answer[:-1]

组合总和

class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:if target < all(_ for _ in candidates):return []self.len = len(candidates)self.result = []self.answer = []self.sum = 0self.backtrack(candidates, target, 0)return self.resultdef backtrack(self, candidates, target, start_index):if self.sum == target:self.result.append(self.answer[:])returnif self.sum > target:returnfor i in range(start_index, self.len):self.answer.append(candidates[i])self.sum += candidates[i]# 这里你可以从该点选无数次,但是之后要从后面选,不能选了后面的之后又从前面选,这样就会重复# 如果不从i开始,会有重复的answer出现self.backtrack(candidates, target, i)self.sum -= candidates[i]self.answer.pop()

组合总和ii

class Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:if target < all(_ for _ in candidates):return []self.len = len(candidates)self.result = []self.answer = []self.sum = 0# 提前排序candidates.sort()self.backtrack(candidates, target, 0)return self.resultdef backtrack(self, candidates, target, start_index):if self.sum == target:self.result.append(self.answer[:])returnif self.sum > target:returnfor i in range(start_index, self.len):# 不能有相同的组合,意味着同一层不能使用相同的元素# 跳过同一层中已经使用过的元素if i > start_index and candidates[i] == candidates[i - 1]:continueself.answer.append(candidates[i])self.sum += candidates[i]self.backtrack(candidates, target, i + 1)self.sum -= candidates[i]self.answer.pop()
# 用used去重
class Solution:def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:if target < all(_ for _ in candidates):return []self.len = len(candidates)self.result = []self.answer = []self.sum = 0self.used = [False] * len(candidates)candidates.sort()self.backtrack(candidates, target, 0)return self.resultdef backtrack(self, candidates, target, start_index):if self.sum == target:self.result.append(self.answer[:])returnif self.sum > target:returnfor i in range(start_index, self.len):# 检查同一树层是否出现曾经使用过的相同元素# 若数组中前后元素值相同,但前者却未被使用(used == False),说明是for loop中的同一树层的相同元素情况if i > 0 and candidates[i] == candidates[i - 1] and self.used[i - 1] == False:continueself.answer.append(candidates[i])self.sum += candidates[i]self.used[i] = Trueself.backtrack(candidates, target, i + 1)self.used[i] = Falseself.sum -= candidates[i]self.answer.pop()

分割回文串

class Solution:def partition(self, s: str) -> List[List[str]]:self.res = []self.answer = []self.backtrack(s, 0)return self.resdef backtrack(self, s, start_index):# 这里只answer只存回文,如果不是回文就continue,所有当start_index走到最后,answer里的都是回文,而且已经全部切割完成了if start_index >= len(s):self.res.append(self.answer[:])return for i in range(start_index, len(s)):if self.is_palindrome(s, start_index, i):self.answer.append(s[start_index: i + 1])self.backtrack(s, i + 1)self.answer.pop()else:continuedef is_palindrome(self, s, start, end):left = startright = endwhile left < right:if s[left] != s[right]:return Falseleft += 1right -= 1return True

复原IP地址

class Solution:def restoreIpAddresses(self, s):if len(s) > 16:return []self.res = []self.answer = ''self.backtrack(s, 0, 0)return self.resdef backtrack(self, s, start_index, times):if times == 3:if len(s[start_index:]) > 3:returnself.answer += s[start_index:]if self.is_vaild_ip(self.answer):self.res.append(self.answer[:])returnfor i in range(start_index, min(len(s), start_index+3)):self.answer += s[start_index: i + 1] + '.'self.backtrack(s, i + 1, times + 1)self.answer = self.answer[: start_index + times]def is_vaild_ip(self, s):ips = list(s.split('.'))for ip in ips:if ip:if int(ip) > 255:return Falseif len(ip) != 1 and ip[0] == '0':return Falseelse:return Falsereturn True

子集问题

class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []self.backtrack(nums, 0)return self.resdef backtrack(self, nums, start_index):self.res.append(self.answer[:])if start_index == len(nums):returnfor i in range(start_index, len(nums)):self.answer.append(nums[i])self.backtrack(nums, i + 1)self.answer.pop()

子集问题II

class Solution:def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []nums.sort()self.backtrack(nums, 0)return self.resdef backtrack(self, nums, start_index):self.res.append(self.answer[:])if start_index == len(nums):returnfor i in range(start_index, len(nums)):if i > start_index and nums[i] == nums[i - 1]:continueself.answer.append(nums[i])self.backtrack(nums, i + 1)self.answer.pop()

递增序列

用useg去重

class Solution:def findSubsequences(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []self.backtrack(nums, 0)return self.resdef backtrack(self, nums, start_index):if len(self.answer) > 1:self.res.append(self.answer[:])if start_index == len(nums):returnuseg = set()for i in range(start_index, len(nums)):if self.answer and nums[i] < self.answer[-1] or nums[i] in useg:continueuseg.add(nums[i])self.answer.append(nums[i])self.backtrack(nums, i + 1)self.answer.pop()

全排列

class Solution:def permute(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []self.backtrack(nums)return self.resdef backtrack(self, nums):if not nums:self.res.append(self.answer[:])for i in range(len(nums)):self.answer.append(nums[i])_pop = nums.pop(i)self.backtrack(nums)nums.insert(i, _pop)self.answer.pop()

最优:

class Solution:def permute(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []self.backtrack(nums)return self.resdef backtrack(self, nums):if len(nums) == len(self.answer):self.res.append(self.answer[:])for i in range(len(nums)):if nums[i] in self.answer:continueself.answer.append(nums[i])self.backtrack(nums)self.answer.pop()
class Solution:def permute(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []self.useg = []self.backtrack(nums)return self.resdef backtrack(self, nums):if len(nums) == len(self.answer):self.res.append(self.answer[:])for i in range(len(nums)):if nums[i] in self.useg:continueself.useg.append(nums[i])self.answer.append(nums[i])self.backtrack(nums)self.answer.pop()self.useg.pop()

全排列II

class Solution:def permuteUnique(self, nums: List[int]) -> List[List[int]]:self.res = []self.answer = []nums.sort()self.used = [0] * len(nums)self.backtrack(nums)return self.resdef backtrack(self, nums):if len(nums) == len(self.answer):self.res.append(self.answer[:])returnfor i in range(len(nums)):if not self.used[i]:# 这里的意思是每种值按顺序选取,但是选取值的顺序随机if i > 0 and nums[i] == nums[i - 1] and not self.used[i - 1]:continueself.used[i] = 1self.answer.append(nums[i])self.backtrack(nums)self.answer.pop()self.used[i] = 0

重新安排行程

from collections import defaultdictclass Solution:def findItinerary(self, tickets: List[List[str]]) -> List[str]:self.path = ['JFK']self.len_tickets = len(tickets)self.ticket_dict = defaultdict(list)for _, ticket in enumerate(tickets):self.ticket_dict[ticket[0]].append(ticket[1])self.Travel('JFK')return self.pathdef Travel(self, start):if len(self.path) == self.len_tickets + 1:return Trueself.ticket_dict[start].sort()for _ in self.ticket_dict[start]:end = self.ticket_dict[start].pop(0)self.path.append(end)if self.Travel(end):  # 这里找到一个就返回 path, 设计的就很巧妙return Trueself.path.pop()self.ticket_dict[start].append(end)

N皇后

class Solution:def solveNQueens(self, n: int) -> List[List[str]]:self.n = nself.result = []# 这里有个问题:不能写成[['.'] * n] * n, 不然后面赋值的时候会出问题self.chess_table = [['.'] * n for _ in range(n)]self.backtrack(0)return self.resultdef backtrack(self, row):if row == self.n:answer = []for item in self.chess_table:answer.append(''.join(item))self.result.append(answer[:])returnfor col in range(self.n):if not self.is_vaild(row, col):continueself.chess_table[row][col] = 'Q'self.backtrack(row + 1)self.chess_table[row][col] ='.'def is_vaild(self, row, col):# 列for i in range(self.n):if self.chess_table[i][col] == 'Q':return False# 左上角i, j = row - 1, col - 1while i >=0 and j >=0 :if self.chess_table[i][j] == 'Q':return Falsei -= 1j -= 1# 右上角i, j = row - 1, col + 1while i >=0 and j < self.n:if self.chess_table[i][j] == 'Q':return Falsei -= 1j += 1return True

解数独

class Solution:def solveSudoku(self, board: List[List[str]]) -> None:"""Do not return anything, modify board in-place instead."""self.backtrack(board)def backtrack(self, board):for i in range(9):for j in range(9):if board[i][j] != '.':continuefor num in range(1, 10):if self.is_vaild(i, j, num, board):board[i][j] = str(num)if self.backtrack(board): return Trueboard[i][j] = '.'# 若1到9填入都没有用,则无解return False# 如果走到了最后一个就返回 Truereturn Truedef is_vaild(self, row, col, num, board):for i in range(9):if board[row][i] == str(num):return Falsefor i in range(9): if board[i][col] == str(num):return Falsespace_row = row // 3space_col = col // 3for i in range(space_row * 3, (space_row + 1) * 3):for j in range(space_col * 3, (space_col + 1) * 3):if board[i][j] == str(num):return False  return True

python 回溯算法总结相关推荐

  1. python回溯算法_什么是回溯法,Python解法交流?

    只有去多做题,才能慢慢掌握.力扣​leetcode-cn.com LeetCode 上的解释 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 ...

  2. Python 回溯算法

    回溯算法(试探法) 在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯"返回,尝试别的路径.回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时 ...

  3. python回溯算法_回溯算法经典问题及python代码实现

    2. 0-1背包问题 # 0-1 bag problem import sys def f(no, cur_mass, things, num): global cur_max if no == nu ...

  4. python回溯算法

    回溯算法:一种优先搜索算法(试探法):按优条件向前搜索,以达目标:当试探到某步,发现原来选择并不好(走不通),就退回重新选择. 回溯算法的一般步骤:1:定义问题的解空间(搜索中动态生成):2:确定易搜 ...

  5. 回溯python_用Python回溯算法

    我试图实现一个算法,该算法需要两个in和n和k,其中n是连续的座位数,k是试图坐在那一行的学生人数.问题在于每个学生在两边都必须至少有两个座位.我有一个函数可以生成所有的子集(一个0或1的数组,1表示 ...

  6. python回溯算法全排列_python 回溯法 子集树模板 系列 —— 11、全排列

    问题 实现 'a', 'b', 'c', 'd' 四个元素的全排列. 分析 这个问题可以直接套用排列树模板. 不过本文使用子集树模板.分析如下: 一个解x就是n个元素的一种排列,显然,解x的长度是固定 ...

  7. 八十四、Python | Leetcode回溯算法系列

    @Author:Runsen @Date:2020/7/7 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  8. python 动态规划 回溯_回溯算法 - 全排列算法实现(pythondart)

    回溯算法 , 就是 穷举 解决一个回溯问题,实际上就是一个决策树的遍历过程. 路径: 也就是已经做出的选择 选择列表: 也就是你当前可以做的选择 结束条件: 也就是到达决策树底层,无法再做选择的条件. ...

  9. python数独伪代码回溯法_数独 #回溯算法 #CTF

    1. intro:巅峰极客的一道逆向 刷巅峰极客2020里的rev题fu!kpy,复杂得不行但是看到if d[1][0] != '8' or d[1][7] != '2'和if check(h1) ! ...

最新文章

  1. 【PC工具】几个电脑录屏相关软件,手机投屏电脑,电脑显示手机摄像头图像,必须好用无广告!...
  2. 为什么一点onclick按钮就提交表单?
  3. mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
  4. 网页字段位置php改变,php实现子字符串位置相互对调互换的方法
  5. Spring 基础概念——DI、IOC(一)
  6. Rust: Rust Language Cheat Sheet,强烈推荐!
  7. android开机动画 制作工具,android开机动画制作
  8. 淘宝网的架构演化分析
  9. 【IT项目管理】第7章 保证项目质量
  10. Python爬虫:Selenium常用操作,下载youtube视频实例
  11. 3ds Max小白入门小案例|旋转楼梯
  12. php根据手机号码获取归属地,PHP通过API获取手机号码归属地,手机号码
  13. redis.conf文件下载与配置
  14. 培养创造性思维的20个技巧!
  15. 如何在电脑/手机上将JPEG图片保存为PDF?
  16. CS:APP CH02信息的表示和处理知识点总结
  17. mac 更新10.11后,出现command not found的解决办法
  18. 21届毕业生毕业一年内的状态
  19. 寒江独钓-Windows内核安全编程总结
  20. AVI文件规范(更新版)

热门文章

  1. unity查询当前屏幕分辨率
  2. Excel 查重小技巧,适用于office2003
  3. 1218: 青蛙(三)
  4. 使用python打印九九乘法表
  5. python处理数据
  6. 动态规划——钢条切割
  7. 苹果手机备忘录内容怎么发送给好友
  8. 第147篇 笔记-预言机(Oracle)
  9. 数据分析笔记--对NBA球员数据的聚类分析(代码)
  10. keras搭建简单CNN模型实现kaggle比赛数字识别