代码随想录算法训练营第二十九天丨 回溯算法part06

news2024/9/21 0:48:18

回溯总结

对于回溯算法,我们需要知道的是 回溯是递归的副产品,只要有递归就会有回溯,所有回溯法常与二叉树遍历【前中后序遍历】,深搜混在一起,原因是都涉及到的递归。

回溯法 = 暴力搜索,它的效率并不高,只是我们在递归回溯的时候可以进行剪枝,减少他递归的次数与深度。

回溯算法能解决下列问题:

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

回溯法对我来说还算较为容易理解,但是后面的棋盘问题,像 323.重新安排行程、51.N皇后、37.解数独 都没有什么思路,再看过一遍卡哥的视频讲解之后对于这些题脑子里面还是有些理解了,希望二刷甚至三刷的时候能够a出来,那就真的爽了,哈哈哈。

对于回溯算法,卡哥给了一个模板,基本上所有的回溯就会用到:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

组合问题

回溯算法:求组合问题! 这是我初入回溯解决的第一道题目,卡哥在文中给我们列举了k层for循环的例子【暴力解法】,递归其实就是可以通过简短的代码实现n层for循环从而进行暴力解题的。

这题卡哥把回溯问题进行了抽象,抽象成了一个树形结构:

对于上图,我们可以很清楚的了解到:for 循环进行横向树层上的遍历,递归纵向进行树枝上的遍历,通过回溯不断调整结果集,进而收集正确的答案。

优化回溯算法 就只有剪枝这一种方法,如图:

剪枝的精髓:

for循环在寻找起点的时候要有一个范围,如果这个起点到集合终止之间的元素已经不够题目要求的k个元素了,就没有必要搜索了

切割问题

回溯算法:分割回文串 (opens new window)中,讲解切割问题,虽然最后代码看起来好像是一道模板题,但是我们从分析到学会套用这个模板,是比较难的。

如下几个难点:

  • 切割问题其实类似组合问题
  • 如何模拟那些切割线
  • 切割问题中递归如何终止
  • 在递归循环中如何截取子串
  • 如何判断回文

如果想到了用求解组合问题的思路来解决 切割问题本题就成功一大半了,接下来就对着模板照葫芦画瓢。

但后序如何模拟切割线,如何终止,如何截取子串,其实都不好想,最后判断回文算是最简单的了

除了这些难点,本题还有细节,例如:切割过的地方不能重复切割所以递归函数需要传入i + 1

树形结构如下:

131.分割回文串

子集问题

在回溯算法:求子集问题! (opens new window)中讲解了子集问题,在树形结构中子集问题是要收集所有节点的结果,而组合问题是收集叶子节点的结果

如图:

78.子集

本题可以不需要加终止条件,因为startIndex >= nums.size(),本层for循环本来也结束了,因为本来我们就要遍历整棵树。

如果要写终止条件,注意:result.push_back(path);要放在终止条件的上面,如下:

result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉结果
if (startIndex >= nums.size()) { // 终止条件可以不加
    return;
}

排列问题

回溯算法:排列问题! (opens new window)不一样了。

排列是有序的,也就是说 [1,2] 和 [2,1] 是两个集合,这和之前分析的子集以及组合所不同的地方。

可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1,所以处理排列问题就不用使用startIndex了。

如图:

46.全排列

此时可以感受出排列问题的不同:

  • 每层都是从0开始搜索而不是startIndex
  • 需要used数组记录path里都放了哪些元素了

本题used数组即是记录path里都放了哪些元素,同时也用来去重,一举两得。

去重问题

之前都是统一使用used数组来去重的,其实使用set也可以用来去重!

在本周小结!(回溯算法系列三)续集 (opens new window)中给出了子集、组合、排列问题使用set来去重的解法以及具体代码。同时详细分析了 使用used数组去重 和 使用set去重 两种写法的性能差异:

使用set去重的版本相对于used数组的版本效率都要低很多!

原因在回溯算法:递增子序列 (opens new window)中分析过,主要是因为程序运行的时候对hashSet 频繁的add,hashSet t需要做哈希映射(也就是把key通过hash function映射为唯一的哈希值)相对费时间,而且 add 的时候其底层的符号表也要做相应的扩充,也是费时的。

而使用used数组在时间复杂度上几乎没有额外负担!

使用set去重,不仅时间复杂度高了,空间复杂度也高了,在本周小结!(回溯算法系列三) (opens new window)中分析过,组合,子集,排列问题的空间复杂度都是O(n),但如果使用set去重,空间复杂度就变成了O(n^2),因为每一层递归都有一个set集合,系统栈空间是n,每一个空间都有set集合。

那我们可能疑惑 用used数组也是占用O(n)的空间啊?

used数组可是全局变量,每层与每层之间公用一个used数组,所以空间复杂度是O(n + n),最终空间复杂度还是O(n)

性能分析

【卡哥的理解】

子集问题分析:

  • 时间复杂度:O(2^n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为O(2^n)
  • 空间复杂度:O(n),递归深度为n,所以系统栈所用空间为O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为O(n)

排列问题分析:

  • 时间复杂度:O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。
  • 空间复杂度:O(n),和子集问题同理。

组合问题分析:

  • 时间复杂度:O(2^n),组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
  • 空间复杂度:O(n),和子集问题同理。

N皇后问题分析:

  • 时间复杂度:O(n!) ,其实如果看树形图的话,直觉上是O(n^n),但皇后之间不能见面所以在搜索的过程中是有剪枝的,最差也就是O(n!),n!表示n * (n-1) * .... * 1。
  • 空间复杂度:O(n),和子集问题同理。

解数独问题分析:

  • 时间复杂度:O(9^m) , m是'.'的数目。
  • 空间复杂度:O(n^2),递归的深度是n^2

一般说道回溯算法的复杂度,都说是指数级别的时间复杂度,这也算是一个概括吧!

回溯专题汇聚为一张图:


上述是根据卡哥的总结及自己的一些了解的综合,自己的语言组织能力较弱,很多都是直接抄卡哥的,有错误望指正。

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

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

相关文章

FreeRTOS入门教程(事件组概念和函数使用)

文章目录 前言一、事件组概念二、事件组和信号量,队列的区别三、事件组相关函数三、事件组应用示例1.等待多个事件2.任务同步 总结 前言 本篇文章将带大家学习什么是事件组以及如何使用事件组。 一、事件组概念 事件组通常是由一组位(bits&#xff09…

CSS页面基本布局

前提回顾 1. 超文本标记语言(HTML)是一种标记语言,用来结构化我们的网页内容并赋予内容含义; (超文本标记语言(英语:HyperText Markup Language /ˈhaɪpətekst ˈmɑːkʌp ˈlŋɡwɪdʒ /…

Linux小技巧之awk必知必会

Linux Shell 三剑客之一,废话不多说直接上干货。 目录 1. 指定分隔符2.指定打印行数3.打印最后一列4.打印倒数第二列5.匹配输出6.if 判断7.统计列总数值8.时间格式转换 示例数据: 1. 指定分隔符 awk -F | {print $1} log.txt # awk -F | {print $1}…

STM32MP135和STM32MP157的区别

本文介绍了STMicroelectronics公司推出的两款多核处理器STM32MP135和STM32MP157之间的区别,包括主频、集成硬件模块数量、内存大小和电压调节模块等方面。 STMicroelectronics是一家领先的半导体解决方案提供商,在嵌入式系统领域有着丰富的经验。他们…

序列合并

题目描述 有两个长度为 N 的单调不降序列 A,B,在 A,B 中各取一个数相加可以得到 个和,求这 个和中最小的 个。 输入格式 第一行一个正整数 N; 第二行 N 个整数 1…N​。 第三行 N 个整数 1…N​。 输出格式 一行 N 个整数&#xff…

从头开始使用 KNN 进行 KNN 和 MNIST 手写数字识别的初学者指南

坦维佩努穆迪 Kaggle参考: MNIST Perfect 100% using kNN | Kaggle 一、说明 MNIST (“修改后的国家标准与技术研究所”)是事实上的计算机视觉“hello world”数据集。自 1999 年发布以来,这个经典的手写图像数据集一直作为分类算…

编写内联函数求解 2x²+4x+5的值,并用主函数调用该函数

动态内存分配可以根据实际需要在程序运行过程中动态地申请内存空间,这种内存空间的分配和释放是由程序员自己管理的,因此也被称为手动内存分配。 C++ 中,动态内存的分配和释放是通过 new 和 delete 操作符进行的。new 操作符用于在堆内存上为对象动态分配空间,dele…

Python之哈希表-哈希表原理

Python之哈希表-哈希表原理 集合Set 集合,简称集。由任意个元素构成的集体。高级语言都实现了这个非常重要的数据结构类型。Python中,它是可变的、无序的、不重复的元素的集合 初始化 set() -> new empty set objectset(iterable) -> new set …

Docker——如何自定义镜像【将自己的项目制作成镜像】?

目录 前言:我们以前是如何部署项目的? 1、镜像由哪几部分构成的 2、如何手动自定义一个镜像 2.1、Dockerfile 2.2、dockerfile文本文件中,最终要写什么? 2.3、构建镜像 3、案例:部署java项目 4、如何与其他容器…

Python 机器学习入门之ID3决策树算法

系列文章目录 第一章 Python 机器学习入门之线性回归 第一章 Python 机器学习入门之梯度下降法 第一章 Python 机器学习入门之牛顿法 第二章 Python 机器学习入门之逻辑回归 番外 Python 机器学习入门之K近邻算法 番外 Python 机器学习入门之K-Means聚类算法 第三章 Python 机…

华为云HECS云服务器docker环境下安装nacos

华为云HECS云服务器,安装docker环境,查看如下文章。 华为云HECS安装docker-CSDN博客 一、拉取镜像 docker pull nacos/nacos-server二、宿主机创建挂载目录 执行如下命令: mkdir -p /usr/local/nacos/logs mkdir -p /usr/local/nacos/con…

服务端监控要怎么做?

目录 前言 一、Google的四类黄金指标 二、RED方法 三、USE方法 RED方法 vs USE方法 四、监控指标 WEB服务监控 MySQL数据库监控 QPS TPS 最大连接数 缓存监控 总结 前言 众所周知,业界各种大中型软件系统在生产运行时,总会有一些手段来…

ESP32C3 LuatOS TM1650②动态显示累加整数

--注意:因使用了sys.wait()所有api需要在协程中使用 -- 用法实例 PROJECT "ESP32C3_TM1650" VERSION "1.0.0" _G.sys require("sys") local tm1650 require "tm1650"-- 拆分整数,并把最低位数存放在数组最大索引处 loc…

车载网关通信能力解析——SV900-5G车载网关推荐

随着车联网的发展,各类车载设备对车载网关的需求日益增长。车载网关作为车与车、车与路、车与云之间连接的关键设备,其通信能力直接影响整个系统的性能。本文将详细解析车载网关的通信能力,并推荐性价比高的SV900-5G车载网关。 链接直达:https://www.key-iot.com/i…

类与对象-对象特性-构造函数与析构函数

#include<iostream> using namespace std; class Person { public: //构造函数 : //没有返回值,不写void //函数名与类名相同 //构造函数可以有参数,能发生重载 //创建对象时,构造函数会自动调用,而且只调用一次 Person() { cout << &quo…

哈里斯鹰算法优化BP神经网络(HHO-BP)回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Python爬虫:ad广告引擎的模拟登录

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

Ubuntu桌面环境的切换方法

你在找它吗&#xff1f; 国内麒麟、深度等系统虽然界面更炫&#xff0c;但——软件仓库与Ubuntu官方已不兼容。国内系统遇到稳定性问题&#xff0c;还是得拿Ubuntu做参照。今天本来介绍下这款Linux桌面。 为什么在 Ubuntu 上考虑 LXQt&#xff1f; 性能&#xff1a;LXQt设计为…

基于SSM的教务管理系统运行教程

文章目录 1、前期必备1.1、所需软件版本说明1.2、下载源码1.3、下载开发工具1.4、下载JDK并配置环境变量1.5、安装数据库和数据库管理工具1.6、安装配置Maven 2、将SQL文件导入到数据库2.1、新建MySQL连接2.2、新建数据库并导入SQL 3、用Eclipse运行程序3.1、导入educationalMa…

操作系统【OS】操作系统的引导

激活CPU。 激活的CPU读取ROM中的boot程序&#xff0c;将指令寄存器置为BIOS(基本输入输出系统)的第一条指令&#xff0c; 即开始执行BIOS的指令。硬件自检。 启动BIOS程序后&#xff0c;先进行硬件自检&#xff0c;检查硬件是否出现故障。如有故障&#xff0c;主板会发出不同含…