当前位置: 首页 > news >正文

算法通关村第十八关:青铜挑战-回溯是怎么回事

青铜挑战-回溯是怎么回事

回溯,最重要的算法之一
主要解决一些暴力枚举也搞不定的问题,例如组合、分割、子集、排列、棋盘等

从性能角度来看回溯算法的效率并不高,但对于这些暴力都搞不定的算法能出结果就很好了,效率低点没关系

回溯可视为递归的拓展,很多思想和解法都与递归密切相关,对比递归来分析其特征会理解的更深刻

举例说明递归和回溯的区别:
设想一个场景,某猛男想脱单,两种策略:

  1. 递归策略:先与意中人制造偶遇,然后了解人家的情况,然后约人家吃饭,有好感之后尝试拉人家的手,没有拒绝就表白
  2. 回溯策略:先统计周围所有单身女孩,然后一个一个表白,被拒绝就说”我喝醉了“,然后就当啥也没有发生,继续下一个

回溯最大的好处:有非常明确的模板
所有的回溯都是一个大框架,因此透彻理解回溯的框架是解决一切回溯问题的基础

回溯不是万能的,解决的问题也是非常明确的,例如组合、分割、子集、排列、棋盘等
不过这些问题具体处理时又有很多不同

回溯可视为递归的拓展,代码结构特别像深度遍历N叉树
难点:回溯在递归语句之后有个”撤销“的操作。
好比谈了个新女朋友,来你家之前,要将前任的东西赶紧藏起来。回溯也一样,有些信息是前任的,要处理掉才能重新开始。

回溯的模板如下

void backtracking(参数){if(终止条件){存放结果;return;}for(选择本层集合中元素(画成树,就是树节点孩子的大小)){处理节点;backtracking(参数);回溯,撤销处理结果}
}

1. 从N叉树说起

二叉树的前序遍历

class TreeNode:def __init__(self, val):self.val = valself.left = Noneself.right = Nonedef tree_dfs(root):if root is None:returnprint(root.val)tree_dfs(root.left)tree_dfs(root.right)

N叉树的前序遍历

class TreeNode:def __init__(self, val):self.val = valself.children = []def tree_dfs(root):# 递归终止条件if root is None:return# 节点处理print(root.val)# 通过循环,分别遍历N个子树for i in root.children:tree_dfs(i)

回溯模板与N叉树的遍历模板非常像!!!

2. 为什么有的问题暴力枚举也不行

什么问题暴力枚举也不行?

举个例子:

LeetCode77 组合
https://leetcode.cn/problems/combinations/

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

n=4, k=2时,双层暴力枚举

def violent_enumeration():res = []for i in range(1, 5):for j in range(i + 1, 5):res.append((i, j))return resif __name__ == '__main__':print(violent_enumeration())  # [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

n=10, k=3时,三层暴力枚举

def violent_enumeration():res = []for i in range(1, 11):for j in range(i + 1, 11):for k in range(j + 1, 11):res.append((i, j, k))return res

k未知时,循环次数未知,这时暴力枚举就失效了

这就是组合类型问题,除此之外,子集、排列、切割、棋盘等方面都有类似的问题,我们需要找到更好的方式

3. 回溯=递归+局部枚举+手动撤销(放下前任)

继续研究
LeetCode77 组合
https://leetcode.cn/problems/combinations/

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。

n=4, k=2时
在这里插入图片描述

n=5, k=3时
在这里插入图片描述

从图中我们可以发现,元素个数n相当于树的宽度(横向),每个结果的元素个数k相当于树的深度(纵向)
此外还有一下规律

  • 局部枚举:每次都是从类似 [1,2,3,4] 这样的序列进行枚举,越往后枚举范围越小
  • 递归:再看n=5,k=3时图中红色大框部分,执行过程与n=4,k=2处理过程一直,时可以递归的子结构
  • 手动撤销:观察图中可以看到,取3得到[1,2,3]之后,需要将3撤掉,再继续取4得到[1,2,4]
    • 对应的代码操作:
    • 将第一个结果放到 path中,path=[1]
    • 将第二个结果放到 path中,path=[1,2]
    • 将第三个结果放到 path中,path=[1,2,3]
    • 将结果输出,撤销3, path=[1,2]
    • 继续枚举,将第三个结果放到 path中,path=[1,2,4]

综上,可以得到 回溯=递归+枚举+手动撤销

这就是回溯的基本规律,掌握之后就可写出完整的回溯代码了

回溯代码实现

import copyclass Solution:def combine(self, n: int, k: int) -> List[List[int]]:def dfs(k, n, begin, path, res):# 递归终止条件是:path的长度等于kif len(path) == k:res.append(copy.deepcopy(path))return# 枚举:针对一个节点,遍历可能的搜索起点for i in range(begin, n+1):# 像路径变量里添加一个数,就是树枝的值path.append(i)# 搜索起点加1,缩小范围,为下一轮递归做准备,因为不允许出现重复的元素dfs(k, n, i + 1, path, res)# 手动撤销path.pop()res = []if k <= 0 or n < k:return respath = []begin = 1dfs(k, n, begin, path, res)return res

4. 图解为什么有个撤销的操作

暂无,理解了上一小节的 手动撤销 即可

5. 回溯热身-再论二叉树的路径问题

5.1 输出二叉树的所有路径

LeetCode 257
https://leetcode.cn/problems/binary-tree-paths/

思路分析

方法1:深度优先搜索
深度优先搜索就是从根节点开始一直找到叶子结点,这里可以先判断当前节点是不是叶子结点,再决定是不是向下走,如果是叶子结点,我们就增加一条路径。这个之前学习,这里不再赘述

方法2:回溯
从回溯的角度分析,得到第一条路径ABD之后怎么找到第二条路径ABE,这里就是先将D撤销,然后再继续递归就可以了

难点,手动撤销

代码实现

方法1:

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:def search_path(node, path):if not node:return path += str(node.val)if not node.left and not node.right:paths.append(path)else:path += "->"search_path(node.left, path)search_path(node.right, path)paths = []if root:search_path(root, path="")return paths

方法2:回溯

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:def search_path(node, path, paths):if node is None:returnpath.append(str(node.val))if node.left is None and node.right is None:paths.append('->'.join(path))for i in [node.left, node.right]:search_path(i, path, paths)path.pop() # 返回上一层递归时,要让当前路径恢复原样paths = []path = []if root:search_path(root, path, paths)return paths

5.2 路径总和问题

LeetCode 113 路径总和 II
https://leetcode.cn/problems/path-sum-ii/

思路分析

目标:路径总和 targetSum=22

在这里插入图片描述

  • 根节点5
  • 需要左侧或右侧target_sum=22-5=17
  • 继续看左子树node(4),需要node(4)左子树或右子树满足 target_sum=17-4=13
  • 依次类推 … …

代码实现

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import copyclass Solution:def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:def find(node, target_sum, path, paths):if node is None:returnpath.append(node.val)if node.left is None and node.right is None and node.val == target_sum:paths.append(copy.deepcopy(path))target_sum -= node.valfor i in [node.left, node.right]:find(i, target_sum, path, paths)path.pop()paths = []path = []if root:find(root, targetSum, path, paths)return paths

注:不想用copy,也可以用 path[:] 替代

相关文章:

算法通关村第十八关:青铜挑战-回溯是怎么回事

青铜挑战-回溯是怎么回事 回溯&#xff0c;最重要的算法之一 主要解决一些暴力枚举也搞不定的问题&#xff0c;例如组合、分割、子集、排列、棋盘等 从性能角度来看回溯算法的效率并不高&#xff0c;但对于这些暴力都搞不定的算法能出结果就很好了&#xff0c;效率低点没关系…...

【Redis】深入探索 Redis 的数据类型 —— 字符串 string

文章目录 前言一、string 类型的操作命令设置和获取相关命令1. SET 和 GET2. MSET 和 MGET3. SETNX、SETEX、SETPX 计数相关命令1. INCR 和 INCRBY2. DECR 和 DECRBY3. INCRBYFLOAT 字符串操作相关命令1. APPEND2. GETRANGE3. SETRANGE4. STRLEN string 相关命令总结 二、strin…...

Linux操作命令笔记

Linux Linux的字母大小写下载和卸载软件更新查看空间使用情况当前目录所在的位置查看文件中的内容查看目录下的文件重启关机移动文件磁盘管理软件修改权限删除文件或文件夹新建文件夹移动一个文件夹文件重命名编译C和C文件VIM编辑器的相关操作 Linux的字母大小写 Linux的文件以…...

1.8 工程相关解析(各种文件,资源访问

目录 1.8 工程相关解析(各种文件&#xff0c;资源访问) 分类 Android 基础入门教程 本节引言&#xff1a; 1.工程项目结构解析&#xff1a; 1.res资源文件夹介绍&#xff1a; 2.如何去使用这些资源 2.深入了解三个文件&#xff1a; MainActivity.java&#xff1a; 布局…...

unity 前后左右 移动

using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public float moveSpeed 5f; // 移动速度 public float rotateSpeed 180f; // 旋转速度 // Start is called before the firs…...

计算机视觉传统图像处理库opencv的使用

人工智能领域的图像处理分支&#xff0c;整理了计算机视觉传统图像处理库opencv的使用网址链接。 opencv使用范围&#xff0c;主要用在计算机视觉、视频分析、机器学习、医学影像处理、自动驾驶、工业检测、游戏开发上。 1&#xff09;&#xff1a;opencv效果视频 opencv10个应…...

【数据库】通过实例讲清楚,Mongodb的增删查改,分组查询,聚合查询aggregate

目录 一.基础概念 二.数据库的管理 1.创建数据库 2.删除数据库 二.集合的管理 1.显示所有集合 2.创建集合 3.删除当前集合 4.向集合中插入元素 三.文档的管理 1.文档插入 2.文档的更新 3.文档的删除 4.文档查询 &#xff08;1&#xff09;查询基本语法&#xff1…...

vue + video.js 加载多种视频流(HLS、FLV、RTMP、RTSP)

起因&#xff1a; 由于需要在一个项目内接入多种常用的视频流&#xff0c;所以接触到video.js&#xff0c;这里就做个记录。 框架&#xff1a; vue2 video.js videojs-contrib-hls videojs-flvjs-es6 videojs-flash video-js.swf vue安装就不讲了&#xff0c;直接从项目…...

用 Python 微调 ChatGPT (GPT-3.5 Turbo)

用 Python 微调 ChatGPT (GPT-3.5 Turbo) 备受期待的 GPT-3.5 Turbo 微调功能现已推出&#xff0c;并且为今年秋季即将发布的 GPT-4 微调功能奠定了基础。 这不仅仅是一次简单的更新——它是一个游戏规则改变者&#xff0c;为开发人员提供了完美定制人工智能模型的关键解决方案…...

单目标应用:基于蜘蛛蜂优化算法(Spider wasp optimizer,SWO)的微电网优化调度MATLAB

一、微网系统运行优化模型 微电网优化模型介绍&#xff1a; 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、蜘蛛蜂优化算法 蜘蛛蜂优化算法&#xff08;Spider wasp optimizer&#xff0c;SWO&#xff09;由Mohamed Abdel-Basset等人于2023年提出&#xff0c;该…...

2023年7月京东饮料行业数据分析(京东运营数据分析)

饮料消费已成为当下快消品行业里的主力军&#xff0c;随着社会群体喜好的改变、消费群体的不断扩大&#xff0c;可选择的饮料种类越来越多&#xff0c;我国饮料市场的体量也较为庞大。根据鲸参谋电商数据分析平台的数据显示&#xff0c;今年7月份&#xff0c;京东平台饮料的销量…...

执行 JUnit 单元测试前,修改环境变量

同一份代码&#xff0c;在不改变配置文件的情况下&#xff0c;可以连接不同的数据库&#xff0c;进行JUnit测试。 非开发、测试、生产环境的区别。而是 我就站在这里&#xff0c;指哪打哪&#xff01; 避免重复造轮子&#xff0c;参考博文&#xff1a; 使用junit&spri…...

openGauss学习笔记-63 openGauss 数据库管理-资源池化架构

文章目录 openGauss学习笔记-63 openGauss 数据库管理-资源池化架构 openGauss学习笔记-63 openGauss 数据库管理-资源池化架构 本文档主要介绍资源池化架构下的一些最佳实践和使用注意事项&#xff0c;用于支撑对相关特性感兴趣的开发者可以快速部署、实践或进行定制化开发。…...

计算机竞赛 基于深度学习的植物识别算法 - cnn opencv python

文章目录 0 前言1 课题背景2 具体实现3 数据收集和处理3 MobileNetV2网络4 损失函数softmax 交叉熵4.1 softmax函数4.2 交叉熵损失函数 5 优化器SGD6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习的植物识别算法 ** …...

ChatGPT如何应对紧急情况和灾害应对?

ChatGPT是一个文本生成模型&#xff0c;它可以用于各种任务&#xff0c;但在处理紧急情况和灾害应对方面&#xff0c;它有一些潜在的用途和限制。在这篇文章中&#xff0c;我们将讨论ChatGPT在紧急情况和灾害应对中的应用&#xff0c;以及如何充分利用这一技术&#xff0c;并提…...

ElementUI浅尝辄止37:Select 选择器

当选项过多时&#xff0c;使用下拉菜单展示并选择内容。 1.如何使用&#xff1f;基础单选 v-model的值为当前被选中的el-option的 value 属性值 <template><el-select v-model"value" placeholder"请选择"><el-optionv-for"item in …...

PCL 基于任意四点计算球心坐标

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 继续基于之前的思路PCL 基于三个点计算圆心坐标之二(二维),假设存在四个不共面的点, ( x 1 , y 1 ) (x_1,y_1)...

飞书即时消息无需API开发连接Cohere,打造飞书AI智能问答助手

飞书即时消息用户使用场景&#xff1a; 许多企业都在使用飞书系统进行协同办公&#xff0c;而现在有了Cohere大语言模型技术&#xff0c;能够根据用户的提问来自动产生回答&#xff0c;无需人为干预。对于企业负责人来说&#xff0c;他们认为如果将Cohere技术融入到飞书机器人中…...

FPGA实现Cordic算法——向量模式

FPGA实现Cordic算法——向量模式 FPGA实现Cordic算法——向量模式1.cordic算法基本原理2.FPGA实现cordic算法向量模式i、FPGA串行实现cordicii、FPGA流水线实现cordiciii、实验结果 FPGA实现Cordic算法——向量模式 1.cordic算法基本原理 FPGA中运算三角函数&#xff0c;浮点数…...

【常用代码14】el-input输入框内判断正则,只能输入数字,过滤汉字+字母。

问题描述&#xff1a; el-input输入框&#xff0c;只能输入数字&#xff0c;但是不能显示输入框最右边的上下箭头&#xff0c; <el-input v-model"input" type"number" placeholder"请输入内容" style"width: 200px;margin: 50px 0;&…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...