回溯算法题模板与实战详解

文章目录

      • 回溯算法模板
      • 实战详解
        • 全排列问题
        • N皇后问题
      • 组合总和问题
      • 子集问题
      • 括号生成问题
      • 单词搜索问题

回溯算法是一种通过试错的方式来寻找问题解决方案的算法,常用于解决约束满足问题、组合优化问题和排列组合问题。其核心思想是深度优先搜索(DFS)与剪枝策略的结合,即通过探索问题的解空间树,当发现当前路径不符合要求时及时“回溯”,撤销之前的部分决策,换一个分支继续搜索。

回溯算法模板

回溯算法的一般框架如下:

def backtrack(当前状态, 选择列表):
    # 基准情形:如果满足结束条件,则处理当前状态,如打印结果或添加到结果列表
    if 满足结束条件:
        处理当前状态
        return
    
    # 选择:基于当前状态做出选择,进入下一层决策树
    for 选择 in 选择列表:
        # 做出选择
        make_choice(当前状态, 选择)
        
        # 递归:在新状态下继续搜索
        backtrack(当前状态, 选择列表)
        
        # 撤销选择:回到上一层决策树,撤销之前的选择
        undo_choice(当前状态, 选择)

实战详解

全排列问题

问题描述:给定一个不含重复数字的整型数组 nums,返回它所有可能的全排列。

解法思路

  • 初始化:定义一个空列表 res 用来存储结果,以及一个列表 track 用来记录当前路径。
  • 终止条件:当 track 的长度等于 nums 的长度时,说明已找到一个排列,将其添加到结果列表 res 中。
  • 选择列表:对于每个数字,可以选择或不选择,但由于不能重复选择,故直接遍历 nums 即可。
  • 递归调用:在每次循环中选择一个数字加入 track,然后递归调用 backtrack 函数。
  • 回溯:在递归返回后,将刚刚加入的数字从 track 中移除,以便于进行下一个数字的选择。

Python代码示例

def permute(nums):
    def backtrack(first = 0):
        # 如果路径长度等于nums的长度,说明已经找到一个解
        if first == n:  
            res.append(track[:])
            return
        for i in range(first, n):
            # 动态维护数组
            nums[first], nums[i] = nums[i], nums[first]
            track.append(nums[first])
            # 继续递归构造下一个数
            backtrack(first + 1)
            # 撤销操作
            track.pop()
            nums[first], nums[i] = nums[i], nums[first]
            
    n = len(nums)
    res = []
    track = []
    backtrack()
    return res
N皇后问题

问题描述:在 n × n 的棋盘上放置 n 个皇后,使得任意两个皇后都不在同一行、同一列以及不在对角线上。

解法思路

  • 初始化:定义棋盘 board 和结果列表 res
  • 终止条件:当所有皇后都被放置完毕,即 row 等于 n 时,将当前棋盘状态加入 res
  • 选择列表:在每一行,可以选择每一列放置皇后,但需通过 isValid 函数确保不冲突。
  • 递归调用:在每行选择一个合法列放置皇后后,递归进入下一行。
  • 回溯:如果当前行无法放置皇后,撤销选择,尝试下一列。

Python代码示例

def solveNQueens(n):
    def isValid(row, col):
        # 检查列是否有皇后冲突
        for i in range(row):
            if board[i] == col or \
                board[i] - i == col - row or \
                board[i] + i == col + row:
                return False
        return True

    def backtrack(row=0):
        if row == n:
            res.append(board[:])
            return
        for col in range(n):
            if isValid(row, col):
                board[row] = col
                backtrack(row + 1)
    
    board = [-1]*n
    res = []
    backtrack()
    return [ ["."*i + "Q" + "."*(n-i-1) for i in sol] for sol in res]

以上两个实例展示了回溯算法在实际问题中的应用,通过不断尝试、验证、回溯,最终找出所有符合条件的解。在解决此类问题时,正确管理状态、合理设计剪枝条件是提高算法效率的关键。

接下来让我们看看回溯算法在其他经典问题上的应用:“组合总和”问题和“子集问题”。

组合总和问题

问题描述:给定一个无重复元素的整数数组 candidates 和一个目标整数 target,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。候选数字可以无限制重复被选取。

解法思路

  • 初始化:定义结果列表 res,路径列表 path,以及起始索引 start
  • 终止条件:当路径的和等于目标值时,将路径加入结果列表。
  • 选择列表:从当前起始索引开始,遍历数组,可以选择当前数多次(通过递归调用时保持起始索引不变实现)。
  • 递归调用:在当前路径的和未超过目标值时,继续向下一层决策树探索。
  • 回溯:在递归返回后,从路径中移除最后一个添加的数,相当于回溯至上一步,改变选择。

Python代码示例

def combinationSum(candidates, target):
    def backtrack(start, target, path):
        if target == 0:
            res.append(path)
            return
        if start >= len(candidates) or target < candidates[start]:
            return
        
        for i in range(start, len(candidates)):
            backtrack(i, target - candidates[i], path + [candidates[i]])
    
    res = []
    candidates.sort()  # 排序可以提前剪枝
    backtrack(0, target, [])
    return res

子集问题

问题描述:给定一个不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

解法思路

  • 初始化:定义结果列表 res 和路径列表 track
  • 终止条件:当遍历完整个数组时,将当前路径加入结果列表。
  • 选择列表:对于数组中的每个元素,都有两种选择,选或不选。
  • 递归调用:递归进入下一层时,将当前元素加入路径;递归返回时,从路径中移除当前元素。
  • 回溯:通过在递归返回时从路径中移除最后一个添加的元素来实现回溯。

Python代码示例

def subsets(nums):
    def backtrack(start, path):
        res.append(path[:])  # 拷贝当前路径并加入结果列表
        for i in range(start, len(nums)):
            path.append(nums[i])  # 选择当前元素
            backtrack(i + 1, path)  # 递归进入下一层
            path.pop()  # 回溯,撤销选择

    res = []
    backtrack(0, [])  # 从第一个元素开始考虑
    return res

通过这些例子,我们可以看到回溯算法的核心思想在于深度优先探索解空间,并通过剪枝和回溯策略有效减少不必要的搜索,从而高效地解决问题。理解这些基本框架和思想后,你就能更好地应对各种回溯算法相关的题目了。

再增加两个回溯算法在另外两个经典问题上的应用:“括号生成”和“单词搜索”,让大家理解更深入些

括号生成问题

问题描述:给定一个整数 n,生成所有合法的括号组合,合法的括号组合需要满足以下条件:左括号必须以正确的顺序闭合,右括号也有正确的顺序。

解法思路

  • 初始化:定义结果列表 res 和路径字符串 curr
  • 终止条件:当路径字符串的长度为 2*n 时,检查是否合法,如果合法则加入结果列表。
  • 选择列表:在任何时候,都可以选择添加一个开括号 ‘(’ 或者在开括号数量多于闭括号数量时添加一个闭括号 ‘)’。
  • 递归调用:在添加一个符号后,递归生成剩余的括号组合。
  • 回溯:在递归返回后,从路径中移除最后添加的括号,恢复到上一步的状态。

Python代码示例

def generateParenthesis(n):
    def backtrack(openN, closeN, curr):
        if openN == closeN == n:
            res.append(curr)
            return
        if openN < n:
            backtrack(openN + 1, closeN, curr + '(')
        if closeN < openN:
            backtrack(openN, closeN + 1, curr + ')')
    
    res = []
    backtrack(0, 0, '')
    return res

单词搜索问题

问题描述:给定一个二维字符网格 board 和一个单词 word,判断单词 word 是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许重复使用。

解法思路

  • 初始化:定义方向列表,用于表示上下左右四个移动方向,以及访问矩阵 visited 记录哪些单元格已经被访问过。
  • 终止条件:当搜索到单词的最后一个字符时,说明找到了合法路径。
  • 选择列表:对于当前位置,可以向四个方向(上、下、左、右)移动。
  • 递归调用:在新位置上继续搜索下一个字符,同时标记当前单元格为已访问。
  • 回溯:在递归返回后,需要取消当前单元格的访问标记,以便于其他路径的探索。

Python代码示例

def exist(board, word):
    def dfs(i, j, k):
        if not 0 <= i < m or not 0 <= j < n or board[i][j] != word[k] or visited[i][j]:
            return False
        if k == len(word) - 1:
            return True
        
        visited[i][j] = True
        for dx, dy in directions:
            if dfs(i + dx, j + dy, k + 1):
                return True
        visited[i][j] = False  # 回溯
        return False
    
    m, n = len(board), len(board[0])
    directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
    visited = [[False]*n for _ in range(m)]
    
    for i in range(m):
        for j in range(n):
            if dfs(i, j, 0):
                return True
    return False

通过这些实例,我们可以进一步体会到回溯算法在解决具有多种可能性或组合问题时的强大之处。掌握这些典型应用,能帮助你在面对复杂问题时更加游刃有余。

😍😍 大量H5小游戏、微信小游戏、抖音小游戏源码😍😍
😍😍试玩地址: https://www.bojiogame.sg😍😍
😍看上哪一款,需要源码的csdn私信我😍

————————————————

​最后我们放松一下眼睛
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Apipost IDEA 插件使用说明

Apipost Helper作为IDEA插件&#xff0c;可以快速生成和查询API文档&#xff0c;直观友好地在IDE中调试接口。它简化了开发流程并提升效率&#xff0c;即使新手也能够迅速掌握。Apipost Helper提供了诸多便捷功能&#xff0c;如通过代码查找接口或者通过接口查找代码等&#xf…

第十五届蓝桥杯物联网试题(国赛)

好&#xff0c;很好&#xff0c;国赛直接来个阅读理解&#xff0c;我猛做4个小时40分钟&#xff0c;cpu都干冒烟了&#xff0c;也算是勉强做完吧&#xff0c;做的很仓促&#xff0c;没多检查就交了&#xff0c;方波不会&#xff0c;A板有个指示灯没做&#xff0c;其他应该都还凑…

师彼长技以助己(3)逻辑思维

师彼长技以助己&#xff08;3&#xff09;逻辑思维 前言 上一篇文章进行了工程思维和产品思维的测试&#xff0c;并介绍了几个比较重要的产品思维模型。接下来本篇介绍工程思维。&#xff08;注意产品思维并不代表产品经理思维&#xff0c;工程思维也并不代表工程师思维&…

学习小心意——python的构造方法和析构方法

构造方法和析构方法分别用于初始化对象的属性和释放类占有的资源 构造方法_init_() 语法格式如下&#xff1a; class 类名:def __init__(self, 参数1, 参数2, ...):# 初始化代码self.属性1 参数1self.属性2 参数2# ... 示例代码如下 class Student:def __init__(self):s…

期权的权利金怎么算的

期权权利金的计算涉及多个因素&#xff0c;包括敲定价格、到期时间以及整个期权合约的具体情况。期权的权利金具体的计算公式和因素可能因不同的期权合约和市场条件而有所不同&#xff0c;下文为大家介绍期权的权利金怎么算的 &#xff1f;本文来自&#xff1a;期权酱 一、期权…

实战:Zig 编写高性能 Web 服务(1)

1.1 认识 std.http std.http 是 Zig 标准库中用于处理 HTTP 相关操作的类库。以我学习新的编程语言的经历来看&#xff0c;编写web程序是最常见的技术场景&#xff0c;所以熟练掌握 HTTP server/client 服务相关的编程知识是比较重要的。 std.http 主要包含以下API: Client…

19、matlab信号预处理中的中值滤波(medfilt1()函数)和萨维茨基-戈雷滤波滤(sgolayfilt()函数)

1、中值滤波&#xff1a;medfilt1()函数 说明&#xff1a;一维中值滤波 1&#xff09;语法 语法1&#xff1a;y medfilt1(x) 将输入向量x应用3阶一维中值滤波器。 语法2&#xff1a;y medfilt1(x,n) 将一个n阶一维中值滤波器应用于x。 语法3&#xff1a;y medfilt1(x,n…

Django中使用ModelForm保存数据

相对来说&#xff0c;使用ModelForm保存数据在Django中算是比较简单的。主要原因是ModelForm是建立在Django的模型&#xff08;Model&#xff09;之上的&#xff0c;它可以自动根据模型的定义生成表单&#xff0c;包括字段和验证规则。这样可以大大简化开发人员处理表单数据的工…

低空经济发展报告

低空经济是指利用低空空间进行商业开发和经济活动的概念。随着航空技术的发展和无人机的普及&#xff0c;低空经济逐渐成为一个新兴的经济领域。 低空经济可以涵盖的领域非常广泛&#xff0c;包括但不限于物流配送、农业植保、城市交通、旅游观光等。利用无人机等飞行器进行物…

C\C++内存管理(未完结)

文章目录 一.C\C内存分布二.C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free三.C内存管理方式3.1.new/delete操作内置类型3.2.new和delete操作自定义类型 四.operator new与operator delete函数&#xff08;重要点进行讲解&#xff09;4.1. operator new与oper…

MySQL—约束—演示(基础)

一、引言 这篇博客主要演示&#xff1a;前面博客在约束概念与分类中讲到的&#xff1a;非空约束、唯一约束、检查约束、默认约束、主键约束、外键约束等等操作。 二、需求 根据下列需求&#xff0c;去完成表结构的创建。 注意&#xff1a;&#xff08;对于一个字段我们可以添…

语言模型解构——Tokenizer

1. 认识Tokenizer 1.1 为什么要有tokenizer&#xff1f; 计算机是无法理解人类语言的&#xff0c;它只会进行0和1的二进制计算。但是呢&#xff0c;大语言模型就是通过二进制计算&#xff0c;让你感觉计算机理解了人类语言。 举个例子&#xff1a;单1&#xff0c;双2&#x…

新农大杏之所向,心之所往团队实地调研与行动

南疆实地调研背景 近期&#xff0c;我团队前往南疆的喀什、和田地区&#xff0c;深入英吉沙县调研“赛买提”杏农民采摘后的收益情况。调研中发现&#xff0c;由于“赛买提”杏属于呼吸跃变型水果&#xff0c;采摘后呼吸作用加剧&#xff0c;加之收获季节易受链格孢侵染引起的…

DevOps后时代,构建基于价值流的平台化工程

本文来自腾讯蓝鲸智云社区用户: CanWay 平台化工程涉及双重核心意义。一方面&#xff0c;是类似利用IDE等工具提高工程师效率的平台化工程&#xff0c;如GitOps或命令行调度般便捷。然而&#xff0c;本文重点探讨的是基于价值流的平台化工程&#xff0c;尤其针对传统金融行业&a…

家宽动态公网IP,使用docker+ddns 实现动态域名解析

官方地址&#xff1a;https://github.com/jeessy2/ddns-go 安装docker docker pull jeessy/ddns-godocker run -d --name ddns-go --restartalways --nethost -v /opt/ddns-go:/root jeessy/ddns-go然后访问ip端口 配置时注意如下

剪画小程序:音频提取:学会这个方法可以提取任何音频!

我是测试了几天&#xff0c;发现是真的好用&#xff0c;所以写了这篇文章给宝子们做下分享 现在各大主流音乐平台都要开通会员才能听取完整版的歌曲&#xff0c; 有些歌甚至只能一个平台上播放&#xff0c;需要来回切换不同的音乐平台十分麻烦 当你正想将这首歌曲收藏到歌单…

常见的激活函数(sigmoid、tanh、ReLU、Leaky ReLU、P-ReLU、R-ReLU、ELU、Swish、Mish、Maxout、softmax)

文章目录 前言求导四则运算法则基本初等函数的导数sigmoid函数sigmoid函数适用场景sigmoid函数图像sigmoid函数的导数公式sigmoid函数的导数图像sigmoid函数的缺点解决办法 tanh函数tanh函数公式推导过程tanh函数图像tanh函数的导数公式tanh函数的导数图像 t a n h ( x ) 1 2…

Java编程常见问题汇总二

系列文章目录 文章目录 系列文章目录前言一、请使用XML解析器二、请使用JDom组装XML三、XML编码陷阱四、未指定字符编码五、未对数据流进行缓存 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击…

Python知识总结

对python知识的梳理&#xff0c;主要是平时的web开发的一些经验。其中比如使用gevent协程&#xff0c;celery异步任务队列,schema,sqlalchemy都是有非常多知识点可以单独讲的。其实python的web开发还有一项我觉得重要的方面是设计模式&#xff0c;这个就可以从其它书里学习了&a…

python小练习03

1.绘制奥运五环旗 #奥运五环的绘制 import turtle as t t.pensize(3) t.speed(0) def draw_circles():i0while i <4:args [[-60,0,"blue"],[0,0,"black"],[60,0,"red"],[-30,-30,"yellow"],[30,-30,"green"]]#定义一个…