Studying-代码随想录训练营day22| 回溯理论基础、77.组合、216.组合总和II、17.电话号码的字母组合

第22天,回溯章节开始!一大算法难点,加油加油!

回溯理论基础组合问题的剪枝操作

文档讲解:代码随想录回溯理论基础

视频讲解:回溯理论基础

回溯法也叫回溯搜索法,它是一种搜索,遍历的方式。回溯是递归的副产品,只要有递归就会有回溯,回溯本质就是递归过程中,到达底端(终止条件)后的不断返回过程。

回溯法的效率:回溯法的本质是穷举,它的效率并不高,是一种暴力解法,但有些时候我们就是需要这种暴力的解法,再适当增加剪枝解决问题。

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

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

这些问题都不好解决,N值较小的情况还能够通过for循环进行遍历,但一旦N值增大,for循环就难以进行编写了,而且在我们无法确定要循环多少层的时候,也无法使用for循环。这个时候就需要使用回溯的方法递归的进行了。

回溯法的结构:回溯法解决的问题都可以抽象为n叉树的树形结构。例如在求集合的问题中,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。因此在运用回溯法解题的时候,我们都可以通过画一个树状图来更好的理解递归的逻辑。

回溯法的模板:回溯法在进行编写的时候,主要需要注意三部,与递归三部曲相同的回溯三部曲。

  • 回溯函数模板返回值以及参数:回溯算法中返回值一般为void,参数列表需要根据你的递归逻辑来一个个确定参数。
void backtracking(参数)
  • 回溯函数终止条件:即满足一定的条件,把答案存储下来,并结束本层递归。回溯函数中肯定需要有终止条件,不能陷入无止尽的递归,那样会导致栈溢出。因此回溯构成的树形结构一定是一个高度有限的树。
if (终止条件) {存放结果;return;
}
  • 回溯搜索的遍历过程:回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。

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

for循环就是遍历集合区间,可以理解为一个节点有多少孩子,这个for循环就执行多少次。

backtracking这里自己调用自己,实现递归。从图中可以看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

总结模板如下:

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

77.组合

文档讲解:代码随想录组合

视频讲解:手撕组合问题、

题目:

学习:本题是回溯算法的第一道题,也是利用回溯算法经典的题目之一。显然对于第一个示例n = 4,k = 2的情况来说,我们可以通过两个for循环很容易的就能得到所有的组合结果,但是当n,k不断增大,至少需要k个for循环才能够遍历所有的节点,直接进行顺序代码编写的时候根本无法进行,因此本题需要采用回溯算法,事实上就是通过递归的方式来进行for循环,本质就是一层递归就是一层for循环。用树形结构来解释本题的回溯过程,如下图:

代码: 

//时间复杂度O(n*2^n)
//空间复杂度O(n)
class Solution {
public://设置全局变量,减少参数数量,避免引用vector<vector<int>> result;vector<int> path;//确定回溯参数,由于是组合问题,组合中元素不存在顺序,因此还需要一个下标来指示当前遍历的位置void backtracking (int n, int k, int idnex) {//确定回溯终止条件if(path.size() == k) {result.push_back(path);return;}//单层递归逻辑for (int i = idnex; i <= n; i++) {path.push_back(i);backtracking(n, k, i + 1);//回溯处理path.pop_back();}}vector<vector<int>> combine(int n, int k) {backtracking(n, k, 1);return result;}
};

本题还可以进行剪枝处理,显然上题中取4这一步是不需要的,因为已经构不成两个数了。当n=4,k=4中不需要的步骤会显得更多:

图中每一个节点就代表本层的for循环,剪枝的关键在于如何确定本层for循环的终点位置。

显然当for循环选择的位置之后的元素个数,不足我们需要的元素个数的时候,就没有必要搜索了。本题中已经选择的元素个数为path.size(),所需需要的元素个数为k - path.size(),列表中剩余元素个数(n - i)需要大于等于所需要的元素个数k - path.size()。因此i最多遍历到n - (k - path.size()) + 1的位置,也即 i <= n - (k - path.size()) + 1,为什么有个+1因为我们要把当前元素加上,这个通过例子模拟一下就能够理解。

因此优化后的for循环应该是:

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

优化后的代码为:

class Solution {
public://设置全局变量,减少参数数量,避免引用vector<vector<int>> result;vector<int> path;//确定回溯参数,由于是组合问题,组合中元素不存在顺序,因此还需要一个下标来指示当前遍历的位置void backtracking (int n, int k, int idnex) {//确定回溯终止条件if(path.size() == k) {result.push_back(path);return;}//单层递归逻辑for (int i = idnex; i <= n - (k - path.size()) + 1; i++) {path.push_back(i);backtracking(n, k, i + 1);//回溯处理path.pop_back();}}vector<vector<int>> combine(int n, int k) {backtracking(n, k, 1);return result;}
};

总结:画图会更有助于我们进行算法的剪枝处理。 


216.组合总和II 

文档讲解:代码随想录组合总和III

视频讲解:手撕组合总和III

题目: 

学习:本题与上题的不同在于遍历的集合确定了是1到9,n和k共同构成了遍历过程中需要判断的条件。本题中k相当于树的深度,9就是树的宽度,假如k=2,n=4用树形结构来表示就是:

代码: 回溯三部曲

//时间复杂度O(n*2^n)
//空间复杂度O(n)class Solution {
public:vector<vector<int>> result; // 存放结果集vector<int> path; // 符合条件的结果void backtracking(int n, int k, int val, int index) { //val已经收集的元素的总和,也就是path里元素的总和//终止条件if(path.size() == k) {if(val == n) {result.push_back(path);}return; // 如果path.size() == k 但sum != targetSum 直接返回}//单层递归逻辑for(int i = index; i <= 9 - (k - path.size()) + 1; i++) {val += i;path.push_back(i);backtracking(n, k, val, i + 1); //注意调整index遍历的集合范围path.pop_back();  //回溯val -= i;  //回溯}}vector<vector<int>> combinationSum3(int k, int n) {backtracking(n, k, 0, 1);return result;}
};

本题同样可以进行剪枝处理,例如上图中,后面有很大部分就不需要进行。

本题可以进行两部分的剪枝。1.首先适合上一道题相同的,依据k进行的集合大小的剪枝,也就是i只能遍历到9 - (k - path.size()) + 1;2.我们可以根据n进行剪枝,当加和val大于n的时候,就可以返回了,不需要继续进行集合后面数字的遍历,因为后面的数字加进来也一定会大于n,已经超出了我们所需要的值。

依据上述两点,代码进行剪枝处理后:

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(int n, int k, int val, int index) {if(path.size() == k) {if(val == n) {result.push_back(path);}return;}//两个剪枝操作//9 - (k - path.size()) + 1 对范围剪枝,不够k个元素就不用遍历for(int i = index; i <= 9 - (k - path.size()) + 1; i++) {val += i;//值剪枝,大于n就不用遍历了if(val > n) {return;}path.push_back(i);backtracking(n, k, val, i + 1);path.pop_back();val -= i;}}vector<vector<int>> combinationSum3(int k, int n) {backtracking(n, k, 0, 1);return result;}
};

17.电话号码的字母组合

文档讲解:代码随想录电话号码的字母组合

视频讲解:手撕电话号码的字母组合

题目:

学习:显然本题也是一个不断循环匹配的过程。依据题干可以把本题分解为3步:1.拆分digits中的数字,并使数字与对应的字母串映射匹配;2.根据顺序依次进行循环遍历。3.保存结果集。例如依据”23“示例,我们可以给出回溯对应的树形图:

对于第1个问题,我们可以通过设置一个哈希表或者map来完成每个数字对字母串的映射,因为本题中数字数量有限且是顺序排列的,因此可以用一个数组来完成映射。

对于2,3问题,就是使用回溯算法来进行模拟遍历了。 

代码: 确定回溯三部曲。

//时间复杂度: O(3^m * 4^n),其中 m 是对应三个字母的数字个数,n 是对应四个字母的数字个数
//空间复杂度: O(3^m * 4^n)
class Solution {
private://使用一个数组来充当哈希表,对应每个字母string letter[10] = {" ", " ", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public:vector<string> result;string s;//确定返回值和参数,参数中需要一个index来指示遍历到第几个数字了void backtracking(string& digits, int index) {//确定终止条件,当index--digits.size()时说明所有的数字都遍历完了,因为最后一个数字的下标为digits.size()-1if(index == digits.size()) {result.push_back(s);return;}//取出数字对应的字符串int dig = digits[index] - '0';string list = letter[dig];//确定单层递归逻辑for(int i = 0; i < list.size(); i++) {s.push_back(list[i]);backtracking(digits, index + 1);s.pop_back();}return; //可写可不写}vector<string> letterCombinations(string digits) {if (digits.size() == 0) {return result;}backtracking(digits, 0);return result;}
};

总结:

回溯算法是一个不好理解的算法,初期过程中可以通过画树形图的方式来加深理解!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shell 编程入门

优质博文&#xff1a;IT-BLOG-CN 【1】x.sh文件内容编写&#xff1a; 固定开头&#xff1a;#&#xff01;/bin/sh&#xff1b; 【2】学习的第一个命令就是echo输出的意思&#xff1b; 【3】其实shell脚本也就是在文件中写命令&#xff0c;但是我们要写的是绝对路径&#xff1a…

鸿蒙开发设备管理:【@ohos.batteryInfo (电量信息)】

电量信息 该模块主要提供电池状态和充放电状态的查询接口。 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import batteryInfo from ohos.batteryInfo;属性 描述电池信息。 系统能…

php基础语法_面向对象

PHP php代码标记 多种标记来区分php脚本 ASP标记&#xff1a;<% php代码 %> 短标记&#xff1a; 脚本标记: 标准标记&#xff08;常用&#xff09;&#xff1a; 简写风格&#xff1a; ASP风格&#xff1a;<% php代码 %> 注意&#xff1a;简写风格和ASP风格…

web前端——HTML

目录 一、HTML概述 1.HTML是什么&#xff1f; 2.HTML具体化解释 二、HTML基本语法 1.声明 2. Head头标签 3.body身体标签 4.一个html的基本结构 5.标签 6.标签属性 ①属性的格式 ②属性的位置 ③添加多个属性 三、基本常用标签 1.超链接 2.图像标签 ①图像标…

SAP ALV 负号提前

FUNCTION CONVERSION_EXIT_ZSIGN_OUTPUT. *"---------------------------------------------------------------------- *"*"本地接口&#xff1a; *" IMPORTING *" REFERENCE(INPUT) *" EXPORTING *" REFERENCE(OUTPUT) *"…

Docker之jekins的安装

jekins官网地址&#xff1a;Jenkins Plugins &#xff08;https://plugins.jenkins.io/&#xff09; jekins 的docker 官方地址&#xff1a;https://hub.docker.com/r/jenkins/jenkins jekins 的docker 允许命令文档地址&#xff1a; docker/README.md at master jenkinsci…

接口自动化测试框架实战(Pytest+Allure+Excel)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1. Allure 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不…

Mac电脑安装HomeBrew工具(100%成功)

1.Homebrew是什么&#xff1f; homebrew是一款Mac OS平台下的软件包管理工具&#xff0c;拥有安装、卸载、更新、查看、搜索等功能。通过简单的指令可以实现包管理&#xff0c;而不用关心各种依赖和文件路径情况。 2.homebrew常用命令 检测是否安装HomeBrew: brew -v卸载Hom…

输出100以内的质数

质数&#xff1a;只能被1和自身整除的数 let count; for(let i2; i<100; i){for(let j1; j<i; j){if(i % j 0){// 只要能被整除&#xff0c;count就加1count;}} if(count 2) {// 从1到自身被整除完之后&#xff0c;如果count只有两次&#xff0c;则说明i为质数co…

【Web3】Web3.js 启动!并解决Web3 is not a constructor报错

苏泽 大家好 这里是苏泽 一个钟爱区块链技术的后端开发者 本篇专栏 ←持续记录本人自学智能合约学习笔记和经验总结 如果喜欢拜托三连支持~ 本节教大家如何启动Web3.js 目录 Web3 启动&#xff01; 于是很愉快的报错 创建实例&#xff01; 出来了 Web3&#xff1a;模块…

【银河麒麟】高可用触发服务器异常重启,处理机制详解

1.服务器环境以及配置 【机型】物理机 处理器&#xff1a; Intel 内存&#xff1a; 126G 【内核版本】 4.19.90-25.16.v2101.ky10.x86_64 【银河麒麟操作系统镜像版本】 Kylin-Server-10-SP2-Release-Shenzhen-Metro-x86-Build01-20220619 Kylin-HA-10-SP2-Release-S…

数据结构_绪论

1.数据结构的研究内容 研究数据的特性和数据之间的关系 用计算机解决一个问题的步骤 1.具体问题抽象成数学模型 实质: 分析问题--->提取操作对象--->找出操作对象之间的关系(数据结构)--->用数学语言描述 操作对象对象之间的关系 2.设计算法 3.编程,调试,运行 …

内容安全复习 1 - 信息内容安全概述

文章目录 信息内容安全简介网络空间信息内容安全大模型 人工智能简介 信息内容安全简介 网络空间 网络空间是融合物理域、信息域、认知域和社会域&#xff0c;控制实体行为的信息活动空间。 上图展示了网络空间安全的结构。可以看到将网络空间划分为了网络域和内容域两个部分。…

PHP-CGI的漏洞(CVE-2024-4577)

通过前两篇文章的铺垫&#xff0c;现在我们可以了解 CVE-2024-4577这个漏洞的原理 漏洞原理 CVE-2024-4577是CVE-2012-1823这个老漏洞的绕过&#xff0c;php cgi的老漏洞至今已经12年&#xff0c;具体可以参考我的另一个文档 简单来说&#xff0c;就是使用cgi模式运行的PHP&…

群晖系统百度网盘套件卸载之后无法再次安装 ContainerManager项目无法删除

前言 最近重新组了个NAS&#xff0c;在套件迁移的时候遇到个头疼的问题。在用矿神的百度网盘在迁移的时候出错了&#xff0c;于是我自己删掉baiduapp得容器和镜像然后卸载套件。不知道中间出了啥问题&#xff0c;套件是已经卸载了&#xff0c;但是群晖ContainerManager套件中的…

GPT-5对普通人有何影响

这篇文章对ChatGPT的使用方法和提问技巧进行了讨论&#xff0c;重点强调了背景信息和具体提问的重要性。文章清晰地传达了如何提高ChatGPT回答的质量&#xff0c;以及个人在使用ChatGPT时的体会和建议。然而&#xff0c;文章在逻辑组织和表达方面还有一些可以改进的地方&#x…

登录安全分析报告:链家地产

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞 …

最年轻获奖者诞生!一文带你了解历届国家最高科学技术奖获奖人

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;4000字丨15分钟阅读 作为国家层面面向科学、技术领域的最高级别奖励&#xff0c;国家最高科学技术奖于 2000 年由国务院设立&#xff0c;每年评选…

Flutter学习目录

学习Dart语言 官网&#xff1a;https://dart.cn/ 快速入门&#xff1a;Dart 语言开发文档&#xff08;dart.cn/guides&#xff09; 学习Flutter Flutter生命周期 点击跳转Flutter更换主题 点击跳转StatelessWidget和StatefulWidget的区别 点击跳转学习Flutter中新的Navigato…

matlab中函数meshgrid

(1) 二维网格 [X,Y] meshgrid(x,y) 基于向量 x 和 y 中包含的坐标返回二维网格坐标。X 是一个矩阵&#xff0c;每一行是 x 的一个副本&#xff1b;Y 也是一个矩阵&#xff0c;每一列是 y 的一个副本。坐标 X 和 Y 表示的网格有 length(y) 个行和 length(x) 个列。 x 1:3; y…