【夜深人静学数据结构与算法】回溯算法

news2024/11/25 10:54:36

目录

前言:

回溯算法:

回溯法的常见应用:

回溯法的模板:

回溯法的图解:​

案例:

77. 组合 - 力扣(LeetCode)

总结:


前言:

        回溯算法是一个比较抽象的算法,因此我们如果初学者,难度可以说是非常大,因此我们利用这篇来讲解回溯算法的理论知识,后续在力扣刷题里面也会详细介绍回溯算法的相关例题。

回溯算法:

回溯算法是一种常见的求解问题的算法。它通常被用来在大量的解空间中搜索所有可能的解,找到所需的解或最优解。

回溯算法通常使用递归来实现。在回溯算法中,递归函数用于在候选解空间中搜索所有可能的解。

回溯算法将问题分解为许多子问题,并递归地对每个子问题进行求解。通过回溯和剪枝,可以避免不必要的计算,提高算法效率。每当算法解决了一个子问题时,它需要回溯到上一个状态,再求解下一个子问题。

因此,回溯算法与递归有密切的关系。实现回溯算法的一种常见方法是使用递归来穷举所有可能的解。递归函数中的状态变量可以充当回溯的历史状态,以便在必要时将其还原到以前的状态。

回溯算法工作流程如下:

1. 定义问题的解空间:定义问题的解空间,并决定要寻找哪些变量或解。

2. 约束条件:针对解空间中的每个子集进行约束条件。

3. 构建候选解:构建候选解。

4. 检查约束:检查每个候选解是否满足约束条件。

5. 解决或回溯:如果候选解满足约束条件,则保存解并继续构建更多解。如果候选解不满足约束条件,则回溯到前一个状态,并继续构建其他候选解。这个过程一直进行,直到找到所需的解或所有的解都被考虑过。

回溯算法常常用于组合优化问题,如旅行商问题、八皇后问题、数独等。其时间复杂度通常为指数级别,因此在解空间较大时,需要谨慎使用。

回溯搜索法其实就是一个纯暴力的搜索,主要是因为部分问题实在太过于复杂,此时使用回溯法暴力搜索如果可以搜索出来就已经是很好的结果了。

回溯算法的特点:

  • 穷举搜索:回溯算法会尝试所有可能的解,因此可以找到所有满足条件的解。
  • 适用范围广:回溯算法可以用于求解组合优化问题、排列组合问题、子集划分问题等各种问题。
  • 可以剪枝优化:通过剪枝操作,可以减少无效的搜索,提高算法的效率。

然而,回溯算法的时间复杂度通常很高,因为它需要遍历大量的可能解。在实际应用中,为了减小搜索空间,可以结合其他优化技巧,如剪枝、启发式搜索等。

 启发式搜索:

        启发式搜索(Heuristic Search)是一种通过使用启发信息来引导搜索方向的搜索算法。它针对问题的特定特征和启发信息(也称为估价函数、启发函数或评估函数),在搜索过程中选择最有希望的路径来达到目标。

        在传统的搜索算法中,如深度优先搜索和广度优先搜索,是盲目地按照某种搜索策略进行搜索,没有充分利用问题本身的特征信息。而启发式搜索不同,它使用问题的特定启发信息来评估搜索状态的优劣性,并根据启发信息指导搜索方向。

        启发函数给出了当前搜索状态的一个估计值,表示该状态与目标之间的距离或优劣程度。通过比较不同状态的启发函数值,启发式搜索算法可以选择具有更优启发函数值的状态作为下一步的搜索目标。这样做的目的是尽量将搜索方向朝向更有希望接近目标的位置。

回溯法的常见应用:

回溯算法通常用于求解组合优化问题组合优化问题的特点是:在给定一组变量的情况下,需要找到满足一定约束条件的最优解或所有解。

77. 组合 - 力扣(LeetCode)

回溯算法常用于解决切割字符串问题,例如如何切割字符串使得切割结果都是回文子串。

剑指 Offer II 086. 分割回文子字符串 - 力扣(LeetCode)

回溯算法常用于解决子集问题,求一个集合的所有子集。

 剑指 Offer II 079. 所有子集 - 力扣(LeetCode)

回溯算法常用于解决排列问题

剑指 Offer 38. 字符串的排列 - 力扣(LeetCode)

 回溯法常用于解n皇后,数独问题。

51. N 皇后 - 力扣(LeetCode)

回溯法的模板:

void backtrack(vector<int>& path, vector<int>& choices) {
    // 满足结束条件,处理结果并返回
    if (满足结束条件) {
        处理结果
        return;
    }
    
    for (int i = 0; i < choices.size(); i++) {
        int choice = choices[i];
        // 跳过无效选择
        if (选择不满足约束条件) {
            continue;
        }
        
        // 选择当前路径
        做出选择
        path.push_back(choice);
        
        // 进入下一层决策树
        backtrack(path, choices);
        
        // 撤销选择
        撤销选择
        path.pop_back();
    }
}

在使用时,需要根据具体问题自行定义结束条件、约束条件、处理结果和选择操作。在backtrack函数中,将问题的路径存储在path中,choices为当前可选择的选项。

需要注意的是,上述模板中的具体代码需要根据实际问题进行实现,包括判断结束条件、约束条件、做出选择和撤销选择等操作。

回溯法的图解:

回溯可以看作一个N叉树结构,因为它的运行过程可以用树的方式进行描述和可视化。

在回溯算法中,我们通过尝试不同的选择和路径来解决问题,直到找到可行的解决方案或者确定无解。这个过程可以看作是在一个多叉树中不断搜索的过程。

下面以一个经典的例子来说明回溯算法的树结构表示:

假设我们要解决一个迷宫问题,迷宫由一个起点和一个终点组成,中间有若干个墙壁。我们可以选择上下左右四个方向中的一个方向前进,直到到达终点或者无路可走。

首先,我们在起点开始,向四个方向中的一个前进,得到四个子节点,分别代表向上、向下、向左、向右四个方向的移动。

对于每个子节点,我们再次尝试向四个方向中的一个前进,得到八个子节点。

以此类推,我们不断生成新的子节点,直到到达终点或者无路可走。

整个过程可以看作是一个树的遍历过程,每个节点代表一个状态,代表当前所在的位置和已经走过的路径。树的根节点是初始状态,叶子节点是最终的解决方案。

此外,回溯算法的树结构还具有回溯的特点,即在搜索过程中,如果发现当前选择导致了无解或者不可行的情况,就返回上一层,撤销当前选择,重新尝试其他选择。这种撤销和回退的操作也可以通过树的退回到上一层的操作来表示。

因此,回溯算法的运行过程可以看作是在一个树结构中进行搜索和回退的过程,通过遍历树的节点来得到问题的解。 

案例:

77. 组合 - 力扣(LeetCode)

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

 这道题其实还是可以用for循环来做,但是问题在于要返回几个数的组合,就要写几个for循环,因为返回几个数是由我们自己决定的。如何控制for循环的个数,就成了本题的关键,而本题给出的方法就是回溯算法。

需要注意的是组合不是排列,组合中[1,2]和[2,1]是一个组合,这也是为什么我们下面2,3,4节点只取后续节点而不往前取。

我们在这里先用图讲解一下基本思路(1234组合,取两个数字作为集合):

回溯的过程就是我们以  1 节点为例,1向下取  12  满足条件,回溯到1,再取13  满足条件  回溯到1,再取14,满足条件,此时1节点已经满足需求了,因此我们在回溯,到初始节点,选择2在进行上述步骤。

class Solution {
private:
    vector<vector<int>> result; // 存放符合条件结果的集合
    vector<int> path; // 用来存放符合条件结果
    void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n; i++) {
            path.push_back(i); // 处理节点
            backtracking(n, k, i + 1); // 递归
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
       
        backtracking(n, k, 1);
        return result;
    }
};

总结:

回溯算法实际上就是递归和for循环的组合,利用递归来自定义了for循环的个数,是一个很不错的思路,并且回溯算法也有自己比较固定的模板,我们在写的时候可以根据这个模板来确定大体框架,补全框架细节,就能得到利用回溯苏纳法解决问题的代码。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

相关文章

JavaScript中的面向对象

面向对象编程是一种编程范式。 面向对象。何为对象&#xff1f; 复习一下&#xff1a; JavaScript中的数据类型分为&#xff1a; 原始类型&#xff1a;数值型&#xff0c;字符串型&#xff0c;布尔型、ES6新增的symbol 特殊类型&#xff1a;undefined型&#xff0c;null型 组合…

chatgpt赋能python:隐藏输入:保护密码和敏感数据的有效方法

隐藏输入&#xff1a;保护密码和敏感数据的有效方法 在今天的数字时代&#xff0c;网络安全成为了无所不在的话题。密码和敏感数据的泄漏是任何人都不想看到的结果。因此&#xff0c;在这种情况下&#xff0c;隐藏输入成为了保护我们的密码和敏感数据的有效方法。 Python是数…

el-date-picker设置右侧显示图标

<template><div><el-form ref"form" label-width"100px"><el-form-item label"日期&#xff1a;" class"date_box"><el-date-picker v-model"time" type"date" :clearable"true&…

【GaussDB分布式特性】

GaussDB分布式特性 实验环境一、分布式架构二、分片和分区 实验环境 华为云GaussDB云数据库&#xff0c;规则如下&#xff1a; 集群拓扑结构&#xff0c;如下&#xff1a; 一、分布式架构 逻辑架构 常用部署方式 部署说明&#xff1a; 1.双AZ采用带第三方仲裁方式部署&am…

利用低代码平台实现协同办公,助力企业提升效益

概要&#xff1a;本文介绍了协同办公的作用&#xff0c;以及利用低代码平台实现协同办公的优势。同时也分享了天翎为华晨汽车打造的低代码协同工具&#xff0c;帮助企业提高管理效率&#xff0c;改善运营模式&#xff0c;提升产品质量及生产精益化。展示了咨询库、原料质量录入…

无锁队列ringbuff实现以及性能

文章目录 一、使用场景二、对比有锁优势三、无锁队列实现方式四、循环队列实现1&#xff09;数据结构设计3&#xff09;代码实现4&#xff09;性能测试5&#xff09;总结 一、使用场景 无锁队列主要适用于高并发场景或者对性能要求较高的场景&#xff0c;主要使用场景有如下几…

pytorch的permute(dims) 函数的功能详解

有三块 二维矩阵 如下 把二维矩阵堆叠起来&#xff0c;就是三维 矩阵 这样的矩阵 从x方向看&#xff08;0维&#xff09; 有三块 记为3&#xff0c;每块从y方向看&#xff08;1维&#xff09;的行是3&#xff0c;从z方向看&#xff08;2维&#xff09;列也是3&#xff0c;故三…

计算机中丢失mfc140.dll怎么解决?mfc140.dll是什么文件?

在计算机运行软件或者游戏的时候&#xff0c;提示mfc140.dll丢失&#xff0c;无法正常启动运行。 mfc140.dll是Windows操作系统中用于支持C编程语言的一个动态链接库&#xff0c;它包含了C运行时库的一些基本功能。用于支持Microsoft Visual Studio的MFC&#xff08;Microsoft …

论文解读:FastSAM | Fast Segment Anything | 基于yolov8-seg实现 比SAM快50倍

发表时间&#xff1a;2023.06.21 论文地址&#xff1a;http://export.arxiv.org/pdf/2306.12156 项目地址&#xff1a;https://github.com/CASIA-IVA-Lab/FastSAM 最近提出的任意分割模型&#xff08;SAM&#xff09;在许多计算机视觉任务中产生了重大影响。它正在成为许多高级…

NIO总结

简要介绍 NIO&#xff08;Non-blocking IO&#xff09;是Java 1.4版本开始引入的一个新的IO API&#xff0c;旨在代替传统IO&#xff0c;它引入了缓冲区和通道的概念&#xff0c;通过选择器实现多路复用。 传统IO会区分字节流InputStream和OutputStream以及字符流Reader和Wri…

GD32 时钟和晶振修改

芯片型号&#xff1a;gd32f470zi 库版本&#xff1a;GD32F4xx_Firmware_Library_V3.0.4 当需要修改外部晶振和主频时需要修改到以下几个地方 1.gd32f4xx.h //此宏定义为我们实际使用的外部晶振频率&#xff0c;此处改为实际使用的25M #define HXTAL_VALUE ((uint32_t)2…

单片机学习 13-I2C_EEPROM

I2C-EEPROM 实验 ​ 这一章我们来学习如何使用 51 单片机的 IO 口模拟 I2C 时序&#xff0c;并实现与AT24C02&#xff08;EEPROM&#xff09;之间的双向通信。开发板板载了 1 个 EEPROM 模块&#xff0c;可实现IIC 通信。本章要实现的功能是&#xff1a;系统运行时&#xff0c…

如何排查 Electron V8 引发的内存 OOM 问题

经过长达大半年时间的崩溃治理后&#xff0c;基于 Electron 框架开发的新版 PC 淘宝直播推流客户端的稳定性终于赶超基于QT 框架开发的旧版本了。剩下的崩溃问题中有 40% 是跟内存 OOM 有关&#xff0c;其中 V8FatalErrorCallback js heap OOM 问题整整困扰了我一个多月。历经千…

Pytorch--模型微调finetune--迁移学习 (待继续学习)

https://www.bilibili.com/video/BV1Z84y1T7Zh/?spm_id_from333.788&vd_source3fd64243313f29b58861eb492f248b34 主要方法 torchvision 微调timm 微调半精度训练 背景&#xff08;问题来源&#xff09; 解决方案 大模型无法避免过拟合&#xff0c;

嵌入式综合性开源项目分享

不定期有小伙伴问&#xff1a; 哪里有好的嵌入式项目&#xff1f; 怎么才能提高编程水平&#xff1f; 有实战项目可以分享一下吗&#xff1f; 目录 第一&#xff1a;嵌入式综合Awesome Embedded 第二&#xff1a;多功能按键MultiButton 第三&#xff1a;软件定时器模块M…

计算机网络学习笔记-应用层

目录 概述 客户-服务器方式&#xff08;C/S&#xff09; 对等方式&#xff08;P2P&#xff09; 域名系统DNS 域名 四种类型 根域名服务器 顶级域名服务器 权限域名服务器 本地名服务器 域名解析过程 万维网WWW 超媒体与超链接 工作方式 URL&#xff08;统一资源…

Spring Boot 中的 WebSocketMessageBrokerConfigurer

Spring Boot 中的 WebSocketMessageBrokerConfigurer 在现代 Web 应用程序中&#xff0c;WebSocket 已成为一种流行的通信协议&#xff0c;它允许客户端和服务器之间实时双向通信。在 Spring Boot 中&#xff0c;我们可以使用 WebSocketMessageBrokerConfigurer 接口来配置 We…

vivo 自研鲁班分布式 ID 服务实践

作者&#xff1a;vivo IT 平台团队- An Peng 本文介绍了什么是分布式ID&#xff0c;分布式ID的业务场景以及9种分布式ID的实现方式&#xff0c;同时基于vivo内部IT的业务场景&#xff0c;介绍了自研鲁班分布式ID服务的实践。 一、方案背景 1.1 分布式ID应用的场景 随着系统的…

【Servlet】HttpServletRequest、HttpServletResponse

目录 &#x1f381;1 HttpServletRequest &#x1f4a5;1.1 通过 query string 来进行传递 &#x1f437;1.2 通过 body (form) 来进行传递 &#x1f6f8;1.3 通过 body(json) 来进行传递 &#x1f358;2. HttpServletResponse &#x1f476;2.1 为响应设置状态码 200 …

JVM 常量池

一、常量池使用 的数据结构 常量池底层使用HashTable key 是字符串和长度生成的hashValue&#xff0c;然后再hash生成index, 改index就是key&#xff1b;Value是一个HashTableEntry&#xff1b; 1、key hashValue hash string(name&#xff0c; len) index hash to…