python实现回溯算法

什么是回溯算法?

回溯算法是一种经典的解决组合优化问题、搜索问题以及求解决策问题的算法。它通过不断地尝试各种可能的候选解,并在尝试过程中搜索问题的解空间,直到找到问题的解或者确定问题无解为止。回溯算法常用于解决诸如排列、组合、子集、棋盘类等问题。

下面是用Python实现回溯算法的示例代码,并对其进行详细解释:

def backtrack(candidate, path, result):if 满足结束条件:  # 如果已经满足结束条件result.append(path[:])  # 将当前路径添加到结果中returnfor 选择 in 候选集:  # 遍历所有候选选择if 当前选择合法:  # 如果当前选择是合法的做出选择  # 将当前选择添加到路径中backtrack(新的候选集, 新的路径, 结果)  # 递归调用,继续探索下一层决策树撤销选择  # 回溯,撤销当前选择,尝试其他选择

上述代码是回溯算法的一般模板。详细解释每个部分的含义:

1.backtrack 函数:这是回溯算法的核心函数。它接受三个参数:candidate 是当前的候选集,path 是当前的路径,result 是保存最终结果的列表。
2.结束条件:在 backtrack 函数的开头,我们先判断是否满足结束条件。如果满足结束条件,即已经找到了一个解,就将当前路径 path 添加到结果 result 中,并立即返回。
3.遍历候选集:在回溯算法中,我们会对当前的候选集进行遍历,尝试每一种可能的选择。
4.合法性检查:在遍历候选集的过程中,我们会对每一个候选选择进行合法性检查,判断当前选择是否符合问题的要求。
5.做出选择:如果当前选择是合法的,我们会将其添加到路径中,表示我们已经做出了一次选择。
6.递归调用:接着,我们会递归调用 backtrack 函数,继续探索下一层决策树。在递归调用中,我们会更新候选集、路径等参数,以便于进行下一层的选择。
7.撤销选择:在递归调用返回后,表示我们已经探索完了当前选择所导致的所有可能性,需要进行回溯。在回溯的过程中,我们会撤销当前选择,尝试其他选择,以便进一步探索其他可能的解。

这就是回溯算法的基本实现思路。通过不断地尝试各种可能的选择,并在尝试过程中搜索解空间,回溯算法能够有效地解决各种组合优化问题和搜索问题。

经典例题

1.N 皇后问题

给定一个 N × N 的棋盘,在棋盘上放置 N 个皇后,使得它们互相不能攻击,即任意两个皇后不能处于同一行、同一列或同一斜线上。

递归回溯:在每一行放置一个皇后,递归地处理下一行的放置。
合法性检查:在放置皇后时,检查当前位置是否与已放置皇后冲突。
回溯撤销:如果当前位置不能放置皇后,则撤销当前选择,尝试其他选择。

def solve_n_queens(n):def is_valid(row, col, queens):for r, c in queens:if r == row or c == col or abs(row - r) == abs(col - c):return Falsereturn Truedef backtrack(row, queens):if row == n:result.append(queens[:])returnfor col in range(n):if is_valid(row, col, queens):queens.append((row, col))backtrack(row + 1, queens)queens.pop()result = []backtrack(0, [])return [['.' * col + 'Q' + '.' * (n - col - 1) for row, col in solution] for solution in result]# 测试
n = 4
print(solve_n_queens(n))

1.solve_n_queens 函数接受一个参数 n,表示棋盘的大小。
2.is_valid 函数用于检查当前位置 (row, col) 是否与已放置的皇后冲突,如果冲突则返回 False,否则返回 True。
3.backtrack 函数是回溯的核心函数,它递归地在每一行放置皇后,并进行合法性检查,如果合法则继续放置下一行的皇后,如果不合法则进行回溯撤销。
最后,返回所有合法的解。每个解使用二维列表表示,其中每个列表元素表示棋盘中一行的布局,‘Q’ 表示放置了皇后的位置,‘.’ 表示空白位置。

2.组合总和

给定一个候选数组 candidates 和一个目标数 target,找出候选数组中所有可以使数字和为目标数的组合。同一个数字可以被选取多次。
递归回溯:在每一层递归中,尝试使用当前候选数组中的数字来组合成目标数。
剪枝优化:在递归过程中,如果当前数字大于目标数,则可以提前结束递归。
去重处理:为了避免重复的组合,我们可以规定每次选择的数字必须不小于上一个选择的数字。

def combination_sum(candidates, target):def backtrack(start, path, target):if target == 0:result.append(path[:])returnfor i in range(start, len(candidates)):if candidates[i] > target:continuepath.append(candidates[i])backtrack(i, path, target - candidates[i])path.pop()result = []candidates.sort()backtrack(0, [], target)return result# 测试
candidates = [2, 3, 6, 7]
target = 7
print(combination_sum(candidates, target))

1.combination_sum 函数接受两个参数:candidates 是候选数组,target 是目标数。
2.backtrack 函数是回溯的核心函数,它接受三个参数:start 表示当前可选的起始索引,path 是当前的组合,target 是当前的目标数。
3.在 backtrack 函数中,如果 target 等于 0,则表示已经找到了一个组合,将当前组合 path 添加到结果列表 result 中。
4.然后,我们遍历候选数组中的数字,并递归调用 backtrack 函数进行下一层的组合。在递归调用中,我们更新 start 参数,以避免重复的组合。
5.如果当前数字大于目标数,则直接跳过,进行剪枝优化。
最后,返回所有找到的组合。

3.全排列

给定一个没有重复数字的序列,返回其所有可能的全排列

递归回溯:在每一层递归中,尝试使用当前可选的数字进行排列。
标记已使用:在递归过程中,需要标记已经使用过的数字,避免重复使用。
处理结果:当达到排列长度时,将当前排列添加到结果列表中。

def permute(nums):def backtrack(path):if len(path) == len(nums):result.append(path[:])returnfor num in nums:if num in path:continuepath.append(num)backtrack(path)path.pop()result = []backtrack([])return result# 测试
nums = [1, 2, 3]
print(permute(nums))

1.permute 函数接受一个参数 nums,表示输入的序列。
2.backtrack 函数是回溯的核心函数,它接受一个参数 path,表示当前的排列。
3.在 backtrack 函数中,如果当前排列长度等于输入序列长度,则表示已经找到一个全排列,将其添加到结果列表 result 中。
4.然后,我们遍历输入序列中的数字,并递归调用 backtrack 函数进行下一层的排列。在递归调用中,我们使用 path 参数来标记已经使用过的数字,避免重复使用。
5.最后,返回所有找到的全排列。

4.子集

给定一个数组,返回其所有可能的子集

递归回溯:在每一层递归中,尝试加入当前元素或不加入当前元素。
处理结果:当递归到底层时,将当前子集添加到结果列表中。

def subsets(nums):def backtrack(start, path):result.append(path[:])for i in range(start, len(nums)):path.append(nums[i])backtrack(i + 1, path)path.pop()result = []backtrack(0, [])return result# 测试
nums = [1, 2, 3]
print(subsets(nums))

5.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合

递归回溯:在每一层递归中,尝试加入当前数字对应的字母。
处理结果:当递归到底层时,将当前字母组合添加到结果列表中。

def letter_combinations(digits):if not digits:return []phone_map = {'2': 'abc', '3': 'def', '4': 'ghi', '5': 'jkl','6': 'mno', '7': 'pqrs', '8': 'tuv', '9': 'wxyz'}def backtrack(index, path):if index == len(digits):result.append(''.join(path))returnfor char in phone_map[digits[index]]:path.append(char)backtrack(index + 1, path)path.pop()result = []backtrack(0, [])return result# 测试
digits = "23"
print(letter_combinations(digits))

6.岛屿数量

给定一个由 ‘0’ 和 ‘1’ 组成的二维网格地图,其中 ‘1’ 表示陆地,‘0’ 表示水域,计算岛屿的数量。岛屿被水域包围,并且水平或垂直相邻(不包含对角线)的陆地被认为是同一个岛屿。
实现思路:
DFS:遍历整个网格,当遇到陆地时,进行深度优先搜索,将与当前陆地相连的所有陆地标记为已访问,直到所有相连的陆地被访问完毕为止。
计数:每次找到一个新的岛屿时,增加岛屿数量。

def num_islands(grid):def dfs(row, col):if row < 0 or row >= len(grid) or col < 0 or col >= len(grid[0]) or grid[row][col] == '0':returngrid[row][col] = '0'  # 标记为已访问for dr, dc in directions:dfs(row + dr, col + dc)if not grid:return 0directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]num_rows, num_cols = len(grid), len(grid[0])num_islands = 0for row in range(num_rows):for col in range(num_cols):if grid[row][col] == '1':num_islands += 1dfs(row, col)return num_islands# 测试
grid = [['1', '1', '0', '0', '0'],['1', '1', '0', '0', '0'],['0', '0', '1', '0', '0'],['0', '0', '0', '1', '1']
]
print(num_islands(grid))

7.单词搜索

给定一个二维网格和一个单词,判断单词是否存在于网格中。字母相邻(包括对角线)的格子组成了单词
实现思路:
对于网格中的每个格子,都作为起点尝试进行深度优先搜索。
在深度优先搜索的过程中,递归地探索当前格子的上、下、左、右四个相邻格子,判断是否能够匹配单词中的下一个字母。
如果能够匹配,继续向下递归搜索;如果不能匹配或者超出了边界,则回溯到上一个格子,尝试其他方向的搜索。
在搜索的过程中,需要使用一个额外的标记数组来记录已经访问过的格子,避免重复访问。

def exist(board, word):def dfs(row, col, index):# 终止条件:单词已全部匹配if index == len(word):return True# 边界条件:越界或当前字母不匹配if row < 0 or row >= len(board) or col < 0 or col >= len(board[0]) or board[row][col] != word[index]:return False# 临时标记当前位置已访问temp = board[row][col]board[row][col] = '#'  # 递归搜索当前字母的上、下、左、右四个相邻格子for dr, dc in [(1, 0), (-1, 0), (0, 1), (0, -1)]:if dfs(row + dr, col + dc, index + 1):return True# 回溯:恢复原始状态board[row][col] = tempreturn Falseif not board or not word:return False# 逐个尝试每个格子作为起点for row in range(len(board)):for col in range(len(board[0])):if dfs(row, col, 0):return Truereturn False# 测试
board = [['A', 'B', 'C', 'E'],['S', 'F', 'C', 'S'],['A', 'D', 'E', 'E']
]
word = "ABCCED"
print(exist(board, word))  # 输出: True

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/734452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

30m二级分类土地利用数据Arcgis预处理及获取

本篇以武汉市为例&#xff0c;主要介绍将土地利用数据转换成武汉市内各区土地利用详情的过程以及分区统计每个区内各地类面积情况&#xff0c;后面还有制作过程中遇到的面积制表后数据过小的解决方法以及一些相关的知识点&#xff1a; 示例数据下载链接&#xff1a;数据下载链…

2024年阿里云服务器新用户购买一个月多少钱?

阿里云服务器一个月多少钱&#xff1f;最便宜5元1个月。阿里云轻量应用服务器2核2G3M配置61元一年&#xff0c;折合5元一个月&#xff0c;2核4G服务器30元3个月&#xff0c;2核2G3M带宽服务器99元12个月&#xff0c;轻量应用服务器2核4G4M带宽165元12个月&#xff0c;4核16G服务…

UnicodeDecodeError: ‘gbk‘和Error: Command ‘pip install ‘pycocotools>=2.0

今天重新弄YOLOv5的时候发现不能用了&#xff0c;刚开始给我报这个错误 subprocess.CalledProcessError: Command ‘pip install ‘pycocotools&#xff1e;2.0‘‘ returned non-zero exit statu 说这个包安装不了 根据他的指令pip install ‘pycocotools&#xff1e;2.0这个根…

哥德巴赫猜想

七十年代末八十年代初&#xff0c;哥德巴赫猜想在中国风靡一时&#xff0c;来源于徐迟的一篇同名报告文学。我还是小孩子&#xff0c;记得大人们叽里咕噜疯传。 “哇&#xff0c;不得了。陈景润证明了1&#xff0b;2&#xff1d;3&#xff0c;离1&#xff0b;1&#xff1d;2就…

misc30

rar解压得到 发现只有中间的图片可以分析&#xff0c;另外两个都有密码 那就先分析星空&#xff0c;属性里面发现 使用该密码可以解压doc文本&#xff0c;发现doc隐写 使用此密码&#xff08;Hello friend!)解压图片,得到一个二维码 扫码得到flag flag{welcome_to_ctfshow}

【Web】浅聊Java反序列化之Rome——关于其他利用链

目录 前言 JdbcRowSetImpl利用链 BasicDataSource利用链 Hashtable利用链 BadAttributeValueExpException利用链 HotSwappableTargetSource利用链 前文&#xff1a;【Web】浅聊Java反序列化之Rome——EqualsBean&ObjectBean-CSDN博客 前言 Rome中ToStringBean的利用…

(001)UV 的使用以及导出

文章目录 UV窗口导出模型的主要事项导出时材质的兼容问题unity贴图导出导出FBX附录 UV窗口 1.uv主要的工作区域&#xff1a; 2.在做 uv 和贴图之前&#xff0c;最好先应用下物体的缩放、旋转。 导出模型的主要事项 1.将原点设置到物体模型的底部&#xff1a; 2.应用修改器的…

java web 知识 jsp

1. jsp: Java Server Page jsp 文件 必须 由 应用服务器 部署(启动项目) ,部署后 用浏览器才能访问 2. jsp 运行原理: idea 编译 部署项目的 位置: C:\Users\74163\AppData\Local\JetBrains\IntelliJIdea2021.1\tomcat\c5b2c807-b7a6-4948-a9ca-71919e55968b 这个 目录 下…

线程和进程

参考链接&#xff1a; 1.基本概念 进程&#xff1a;Windows系统中&#xff0c;一个运行的xx.exe就是一个进程。例如打开浏览器就是一个进程 线程&#xff1a;进程中的一个执行任务&#xff08;控制单元&#xff09;&#xff0c;负责当前进程中程序的执行。一个进程至少有一个…

【Web】关于Java反序列化那些实现机制的朴素通识

目录 原生⚔ 自定义 基于 Java 原生序列化机制&#xff1a; 基于自定义序列化机制&#xff1a; 基于Bean⚔基于Field 基于 Bean 的反序列化&#xff1a; 基于 Field 的反序列化&#xff1a; 常见序列化协议归类 原生⚔ 自定义 基于 Java 原生序列化机制&#xff1a; 特…

Android14 Handle机制

Handle是进程内部, 线程之间的通信机制. handle主要接受子线程发送的数据, 并用此数据配合主线程更新UI handle可以分发Message对象和Runnable对象到主线程中, 每个handle实例, 都会绑定到创建他的线程中, 它有两个作用,: (1) 安排消息在某个主线程中某个地方执行 (2) 安排…

Python学习之基础语法

一、HelloWorld 二、Python基础语法 2.1 字面量 定义&#xff1a;在代码中&#xff0c;被写下来的固定的值&#xff0c;称之为字面量。 常用的6种值的类型 字符串 Python中&#xff0c;字符串需要用双引号包围&#xff1b; 被双引号包围的都是字符串 666 13.14 "黑马…

深度学习预备知识(线性代数)

介绍&#xff1a; 深度学习是一种机器学习的方法&#xff0c;涉及到大量的线性代数运算。线性代数是研究向量空间和线性映射的数学学科。在深度学习中&#xff0c;线性代数常用于表示和处理输入数据和模型参数。下面是一些深度学习中常见的线性代数概念和运算&#xff1a; 1. …

数据结构之单链表及其实现!

目录 ​编辑 1. 顺序表的问题及思考 2.链表的概念结构和分类 2.1 概念及结构 2.2 分类 3. 单链表的实现 3.1 新节点的创建 3.2 打印单链表 3.3 头插 3.4 头删 3.5 尾插 3.6 尾删 3.7 查找元素X 3.8 在pos位置修改 3.9 在任意位置之前插入 3.10 在任意位置删除…

【python量化】基于okex API开发的海龟策略

介绍 基于okex api开发的海龟策略&#xff0c;okex海龟策略python实现方式。该程序目前只支持单品种&#xff0c;比如设置ETH后&#xff0c;只对ETH进行做多做空。该程序运行需要两样东西&#xff1a;apikey 和 标的 运行该程序之前&#xff0c;用户需要到okex网站去申请apiK…

嘉绩咨询:八位一体产业创新,赋能品牌新零售

探索新零售领域不断创新高峰的嘉绩咨询在今天全面展现了其“八位一体”产业创新模式&#xff0c;该模式旨在为新零售品牌提供全方位的赋能服务。立足于广州的企业战略导航专家&#xff0c;吹响了帮助中国品牌实现全球化发展的号角。 嘉绩咨询的核心业务涵盖招商教育、招商落地、…

【C++】6-12 运动成绩排名 分数 10

6-12 运动成绩排名 分数 10 全屏浏览 切换布局 作者 范鹏程 单位 内蒙古师范大学 某大学开田径运动会&#xff0c;现有12名选手参加100米比赛&#xff0c;对应的运动员号及成绩如表所示&#xff0c;请按照成绩排名并输出&#xff0c;要求每一行输出名次、运动员号及成绩。 …

Java学习笔记之IDEA的安装与下载以及相关配置

1 IDEA概述 ​IDEA全称IntelliJ IDEA&#xff0c;是用于Java语言开发的集成环境&#xff0c;它是业界公认的目前用于Java程序开发最好的工具。 集成环境&#xff1a; ​把代码编写&#xff0c;编译&#xff0c;执行&#xff0c;调试等多种功能综合到一起的开发工具。 2 IDEA…

穿戴产品功耗开发——外设篇

上一篇已对主控的硬件和软件层面做了相应的总结&#xff0c;本篇将从手表常见的外设来说明&#xff1a;外设不同状态下对穿戴产品功耗的影响。 手表常见的外设有&#xff1a; 人机交互&#xff1a;LCDTouch等。常见有TFT、AMOLED模组&#xff1b;生物医学传感器&#xff1a;P…

使用TTL直接对esp32-cam进行烧录

首先你要有一个usb转TTL下载器和一个esp32-cam 然后我们要将IO0与GND短接 UOR->TXD UOT->RXD 3V3->3V3 GND->GND