算法基础-回溯算法

回溯算法大致分为以下几类:
组合:组合、组合总和、电话号码的字母组合
分割:分割回文串、复原IP地址
子集:子集
排列:全排列
棋盘问题:N皇后、解数独
其他:递增子序列、重新安排行程

一、什么是回溯算法

回溯算法也可以叫做回溯搜索法,它是一种搜索方法。
回溯是递归的副产品,只要有递归就会有回溯(递归中隐藏着回溯算法)。
所以以下讲解中,回溯函数也就是递归函数,指的都是一个函数。

二、回溯算法解决什么问题

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

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

这里再帮大家回顾一下高中的排列组合知识。
组合和排列的区别是,组合是不强调元素顺序的,而排列强调元素顺序。
举个最简单的例子:[1,2]这个集合,论组合只有[1,2]这一个集合,论排列却有[1,2]、[2,1]这两个集合。

三、如何理解回溯算法

回溯法解决的问题都可以抽象成树形结构,因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,构成了树的深度。递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

四、回溯算法模板

1、回溯函数模板返回值以及参数

回溯算法中函数返回值一般为void。再来看一下参数,因为回溯算法需要的参数不像二叉树递归的时候那么容易一次性确定下来,所以一般先写逻辑,然后需要什么参数,就填什么参数。
回溯函数伪代码如下:

void backtrack(参数)

2、回溯函数终止条件

回溯法是一种树形结构,那么一定就会有终止条件。
什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。
所以回溯函数终止条件伪代码如下:

if (终止条件) {
    存放结果;
    return;
}

3、回溯搜索的遍历过程

回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成了树的深度。
如下图:

在这里插入图片描述

图中特意举例集合大小和孩子数量相等。
回溯函数遍历过程伪代码如下:

for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtrack(路径, 选择列表);  // 递归
    回溯,撤销处理结果
}

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。
backtrack这里自己调用自己,实现递归。
可以从图中看出for循环可以理解是横向遍历,backtrack是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜到的叶子节点就是找的其中的一个结果了。
分析完过程,回溯算法模板框架如下:

void backtrack(参数)
if (终止条件) {
    存放结果;
    return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtrack(路径, 选择列表);  // 递归
    回溯,撤销处理结果
}

五、回溯算法效率

虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法。
因为回溯的本质是穷举,穷举所有的可能,然后选出我们想要的答案,如果想要回溯法高效一些,可以加一些剪枝的操作,但这也改变不了回溯法就是穷举的本质。
那么,既然回溯法并不高效,为什么还要用它呢?因为对于某些问题来讲,只能使用暴力搜索来解决,除此之外没有更高效的解决方法。

六、回溯算法例题

以LeetCode第46题为例来讲解一下回溯算法的具体应用。

46. 全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:

输入:nums = [1]
输出:[[1]]
 
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

这个问题是典型的回溯递归问题,可用如下代码解答:

void swap(int * nums,int index1,int index2)
{
    int temp = nums[index1];
    nums[index1] = nums[index2];
    nums[index2] = temp;
}

void backTrack(int* nums, int numsSize, int* returnSize, int** returnColumnSizes, int** res, int offset)
{
    if(offset == numsSize) {  // 遍历到了末尾
        res[*returnSize] = (int *)malloc(sizeof(int ) * numsSize);
        memcpy(res[*returnSize], nums, sizeof(int) * numsSize );  // 拷贝结果到res中保存
        (*returnColumnSizes)[*returnSize] = numsSize;  // 记录返回数组中每行的列数
        *returnSize = *returnSize + 1;
    } else {
        //回溯算法的核心
        for(int i = offset; i < numsSize; i++) {
            swap(nums, i, offset);  // i 和 offset 交换,如[1,2,3]填入res后,此时步步回溯后,二次循环i为2,off为1,交换后为[1,3,2]
            backTrack(nums, numsSize, returnSize, returnColumnSizes, res, offset+1);
            swap(nums, i, offset);  // 从prem返回之后,也即找到一个答案后,将数组恢复为原来状态,在原状基础上继续遍历,如[1,3,2]填入res后恢复为[1,2,3]
        }
    }
}

int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes)
{
    //不重复的数字的全排序
    //组合次数为 n!= n *( n - 1) *( n - 2) ...... 2 * 1
    //这样的方法适合回溯的方法
    //取值范围1 <= nums.length <= 6  = 6 * 5 * 4 * 3 *2 * 1 = 720中可能
    int **res = (int **)malloc(sizeof(int *) * 721);
    *returnColumnSizes = (int *)malloc(sizeof(int ) * 721);
    *returnSize = 0;
    backTrack(nums, numsSize, returnSize, returnColumnSizes, res, 0);
    return res;
}

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

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

相关文章

gns3:动态路由(ospf) area0 骨干网络(域间)(ABR)+ ospf 连接 rip (外部)(ASBR)+ 区域划分

1.配置好接口ip 全部处于up状态2.配置好lookback口 增加一个虚拟直连网段全部为 255.255.255.0的子网掩码实现上边ospf之间通信r1的全局模式router ospf 1network 192.168.1.0 0.0.0.255 area 1network 1.1.1.0 0.0.0.255 area 1宣告直连 并且划分area 区域为1r2全局模式router…

一种LCD屏闪问题的调试

背景 项目使用ESP32-S3 RGB接口驱动的LCD, 框架 idf-v5.0, LVGL-v7.11 显示画面正常, 但肉眼可见的像是背光在闪烁, 背光电路是应用很久的经典电路, 且排查背光驱动无错, 但开机一段时间后, 闪烁会明显减轻 记录 这块屏的显示驱动芯片为ST7701S, 查看芯片手册有说明特定的上…

全网最完整,接口测试总结彻底打通接口自动化大门,看这篇就够了......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 所谓接口&#xff0…

音视频开发—MediaCodec 解码H264/H265码流视频

使用MediaCodec目的 MediaCodec是Android底层多媒体框架的一部分&#xff0c;通常与MediaExtractor、MediaMuxer、AudioTrack结合使用&#xff0c;可以编码H264、H265、AAC、3gp等常见的音视频格式 MediaCodec工作原理是处理输入数据以产生输出数据 MediaCodec工作流程 Med…

SpringBoot整合Flink(施耐德PLC物联网信息采集)

SpringBoot整合Flink&#xff08;施耐德PLC物联网信息采集&#xff09;Linux环境安装kafka前情&#xff1a;施耐德PLC设备&#xff08;TM200C16R&#xff09;设置好信息采集程序&#xff0c;连接局域网&#xff0c;SpringBoot订阅MQTT主题&#xff0c;消息转至kafka&#xff0c…

计算机网络体系结构——“计算机网络”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰来学习一个全新的知识点&#xff0c;就是计算机网络啦&#xff0c;下面&#xff0c;开始虚心学习。 计算机网络的概念 计算机网络的功能 计算机网络的组成 计算机网络的分类 标准化工作 计算机网络的性能 计算机网络的概念 …

Hadoop集群环境配置搭建

一、简单介绍 Hadoop最早诞生于Cutting于1998年左右开发的一个全文文本搜索引擎 Lucene&#xff0c;这个搜索引擎在2001年成为Apache基金会的一个子项目&#xff0c;也是 ElasticSearch等重要搜索引擎的底层基础。 项目官方&#xff1a;https://hadoop.apache.org/ 二、Linux环…

SpringBoot 结合RabbitMQ与Redis实现商品的并发下单【SpringBoot系列12】

SpringCloud 大型系列课程正在制作中&#xff0c;欢迎大家关注与提意见。 程序员每天的CV 与 板砖&#xff0c;也要知其所以然&#xff0c;本系列课程可以帮助初学者学习 SpringBooot 项目开发 与 SpringCloud 微服务系列项目开发 1 项目准备 SpringBoot 整合 RabbitMQ 消息队…

【前端八股文】浏览器系列:性能优化——HTML、CSS、JS、渲染优化

文章目录HTMLCSSCSS加载会造成阻塞吗JavaScript渲染优化参考本系列目录&#xff1a;【前端八股文】目录总结 是以《代码随想录》八股文为主的笔记。详情参考在文末。 代码随想录的博客_CSDN博客-leecode题解,ACM题目讲解,代码随想录领域博主 性能优化&#xff0c;从以下几个方…

【C++】STL容器、算法的简单认识

几种模板首先认识一下函数模板、类模板、栈模板。函数模板函数模板就是一个模型&#xff0c;而模板函数是函数模板经过类型实例化的函数。如下template<class T>是一个简单的函数模板&#xff1a;template<class T> T Max(T a, T b) {return a > b ? a : b; } …

Joomla未授权访问漏洞CVE-2023-23752

1、前言Joomla是一套全球知名的内容管理系统&#xff08;CMS&#xff09;&#xff0c;其使用PHP语言加上MySQL数据库所开发&#xff0c;可以在Linux、Windows、MacOSX等各种不同的平台上运行。2月16日&#xff0c;Joomla官方发布安全公告&#xff0c;修复了Joomla! CMS中的一个…

cjson文件格式介绍

cjson是一种轻量级的JSON解析库&#xff0c;它支持将JSON格式的数据转换为C语言中的数据结构&#xff0c;同时也支持将C语言中的数据结构转换为JSON格式的数据。cjson的文件格式是指在使用cjson库时&#xff0c;将JSON格式的数据存储在文件中&#xff0c;然后通过cjson库读取文…

C++ 学习笔记(十)(继承、抽象篇)

前言&#xff1a;主要是自己学习过程的积累笔记&#xff0c;所以跳跃性比较强&#xff0c;建议先自学后拿来作为复习用。 文章目录1 定义父类和子类1.1 定义父类访问说明符 protected1.2 定义子类1.3 子类向父类的转换1.4 转换的例外1.5 子类的构造函数1.6 静态成员不能继承1.7…

clip精读

开头部分 1. 要点一 从文章题目来看-目的是&#xff1a;使用文本监督得到一个可以迁移的 视觉系统 2.要点二 之前是 fix-ed 的class 有诸多局限性&#xff0c;所以现在用大量不是精细标注的数据来学将更好&#xff0c;利用的语言多样性。——这个方法在 nlp其实广泛的存在&…

2023年ACM竞赛班 2023.3.20题解

目录 瞎编乱造第一题 瞎编乱造第二题 瞎编乱造第三题 瞎编乱造第四题 瞎编乱造第五题 不是很想编了但还是得编的第六题 不是很想编了但还是得编的第七题 还差三道题就编完了的第八题 还差两道题就编完了的第九题 太好啦终于编完了 为啥一周六天早八阿 瞎编乱造第一题…

【Matlab算法】粒子群算法求解一维线性函数问题(附MATLAB代码)

MATLAB求解一维线性函数问题前言正文函数实现可视化处理可视化结果前言 一维线性函数&#xff0c;也称为一次函数&#xff0c;是指只有一个自变量xxx的函数&#xff0c;且函数表达式可以写成yaxbyaxbyaxb的形式&#xff0c;其中aaa和bbb是常数。具体来说&#xff0c;aaa称为斜…

typedef uint8_t u8;(stm32数据类型)

在stm32单片机的库文件里有这么一段u8和u16的定义 typedef uint8_t u8; typedef uint16_t u16&#xff1b; 而uint8_t和uint16_t的定义是这样的 typedef unsigned char uint8_t; typedef unsigned short int uint16_t; 意味着u8就是就是指代的unsigned char …

linux简单入门

目录Linux简介Linux目录结构Linux文件命令文件处理命令文件查看命令常用文件查看命令Linux的用户和组介绍Linux权限管理Linux简介 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹&#xff0…

【Nginx二】——Nginx常用命令 配置文件

Nginx常用命令 配置文件常用命令启动和重启 Nginx配置文件maineventshttp常用命令 安装完成nginx后&#xff0c;输入 nginx -&#xff1f;查询nginx命令行参数 nginx version: nginx/1.22.1 Usage: nginx [-?hvVtTq] [-s signal] [-p prefix][-e filename] [-c filename] [-…

[数据结构]直接插入排序、希尔排序

文章目录排序的概念和运用排序的概念排序运用常见的排序算法常见的排序算法直接插入排序希尔排序性能对比排序的概念和运用 排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操…