代码随想录学习摘抄day9(回溯1-11)

一个朴实无华的目录

  • 定义:回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
    • 应用场景:回溯法解决的问题都可以抽象为树形结构
    • 代码模板
  • 题型
    • 第77题. 组合
      • 思路:每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围。
    • 216.组合总和III
      • 思路:本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。
    • 17.电话号码的字母组合
      • 思路:求不同集合之间的组合
    • 39. 组合总和
      • 思路:
    • 40.组合总和II
      • 思路:加一个bool型数组used,用来记录同一树枝上的元素是否使用过。实现去重
    • 131.分割回文串
      • 思路:
    • 93.复原IP地址
      • 思路:切割问题就可以使用回溯搜索法把所有可能性搜出来
    • 78.子集
      • 思路:子集问题是找树的所有节点

定义:回溯法也可以叫做回溯搜索法,它是一种搜索的方式。

回溯是递归的副产品,只要有递归就会有回溯。

应用场景:回溯法解决的问题都可以抽象为树形结构

集合的大小就构成了树的宽度,递归的深度就构成了树的深度。
组合问题:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等

代码模板

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

题型

第77题. 组合

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

示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

思路:每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围。

图中可以发现n相当于树的宽度,k相当于树的深度。
图中每次搜索到了叶子节点,我们就找到了一个结果。
把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。

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) {result.clear(); // 可以不写path.clear();   // 可以不写backtracking(n, k, 1);return result;}
};

216.组合总和III

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

思路:本题k相当于树的深度,9(因为整个集合就是9个数)就是树的宽度。

一维数组path来存放符合条件的结果,二维数组result来存放结果集。

class Solution {
private:vector<vector<int>> result; // 存放结果集vector<int> path; // 符合条件的结果// targetSum:目标和,也就是题目中的n。// k:题目中要求k个数的集合。// sum:已经收集的元素的总和,也就是path里元素的总和。// startIndex:下一层for循环搜索的起始位置。void backtracking(int targetSum, int k, int sum, int startIndex) {if (path.size() == k) {if (sum == targetSum) result.push_back(path);return; // 如果path.size() == k 但sum != targetSum 直接返回}for (int i = startIndex; i <= 9; i++) {sum += i; // 处理path.push_back(i); // 处理backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndexsum -= i; // 回溯path.pop_back(); // 回溯}}public:vector<vector<int>> combinationSum3(int k, int n) {result.clear(); // 可以不加path.clear();   // 可以不加backtracking(n, k, 0, 1);return result;}
};

17.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

思路:求不同集合之间的组合

class Solution {
private:const string letterMap[10] = {"", // 0"", // 1"abc", // 2"def", // 3"ghi", // 4"jkl", // 5"mno", // 6"pqrs", // 7"tuv", // 8"wxyz", // 9};
public:vector<string> result;string s;void backtracking(const string& digits, int index) {if (index == digits.size()) {result.push_back(s);return;}int digit = digits[index] - '0';        // 将index指向的数字转为intstring letters = letterMap[digit];      // 取数字对应的字符集for (int i = 0; i < letters.size(); i++) {s.push_back(letters[i]);            // 处理backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了s.pop_back();                       // 回溯}}vector<string> letterCombinations(string digits) {s.clear();result.clear();if (digits.size() == 0) {return result;}backtracking(digits, 0);return result;}
};

39. 组合总和

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入:candidates = [2,3,6,7], target = 7,
所求解集为: [ [7], [2,2,3] ]
示例 2:

输入:candidates = [2,3,5], target = 8,
所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ]

思路:

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum > target) {return;}if (sum == target) {result.push_back(path);return;}for (int i = startIndex; i < candidates.size(); i++) {sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates, target, sum, i); // 不用i+1了,表示可以重复读取当前的数sum -= candidates[i];path.pop_back();}}
public:vector<vector<int>> combinationSum(vector<int>& candidates, int target) {result.clear();path.clear();backtracking(candidates, target, 0, 0);return result;}
};

40.组合总和II

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。

思路:加一个bool型数组used,用来记录同一树枝上的元素是否使用过。实现去重

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {if (sum == target) {result.push_back(path);return;}for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {// used[i - 1] == true,说明同一树枝candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过// 要对同一树层使用过的元素进行跳过if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {continue;}sum += candidates[i];path.push_back(candidates[i]);used[i] = true;backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次used[i] = false;sum -= candidates[i];path.pop_back();}}public:vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {vector<bool> used(candidates.size(), false);path.clear();result.clear();// 首先把给candidates排序,让其相同的元素都挨在一起。sort(candidates.begin(), candidates.end());backtracking(candidates, target, 0, 0, used);return result;}
};

131.分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]

思路:

class Solution {
private:vector<vector<string>> result;vector<string> path; // 放已经回文的子串void backtracking (const string& s, int startIndex) {// 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了if (startIndex >= s.size()) {result.push_back(path);return;}for (int i = startIndex; i < s.size(); i++) {if (isPalindrome(s, startIndex, i)) {   // 是回文子串// 获取[startIndex,i]在s中的子串string str = s.substr(startIndex, i - startIndex + 1);path.push_back(str);} else {                                // 不是回文,跳过continue;}backtracking(s, i + 1); // 寻找i+1为起始位置的子串path.pop_back(); // 回溯过程,弹出本次已经添加的子串}}bool isPalindrome(const string& s, int start, int end) {for (int i = start, j = end; i < j; i++, j--) {if (s[i] != s[j]) {return false;}}return true;}
public:vector<vector<string>> partition(string s) {result.clear();path.clear();backtracking(s, 0);return result;}
};

93.复原IP地址

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。

思路:切割问题就可以使用回溯搜索法把所有可能性搜出来

在这里插入图片描述
startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。

本题我们还需要一个变量pointNum,记录添加逗点的数量。

class Solution {
private:vector<string> result;// 记录结果// startIndex: 搜索的起始位置,pointNum:添加逗点的数量void backtracking(string& s, int startIndex, int pointNum) {if (pointNum == 3) { // 逗点数量为3时,分隔结束// 判断第四段子字符串是否合法,如果合法就放进result中if (isValid(s, startIndex, s.size() - 1)) {result.push_back(s);}return;}for (int i = startIndex; i < s.size(); i++) {if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法s.insert(s.begin() + i + 1 , '.');  // 在i的后面插入一个逗点pointNum++;backtracking(s, i + 2, pointNum);   // 插入逗点之后下一个子串的起始位置为i+2pointNum--;                         // 回溯s.erase(s.begin() + i + 1);         // 回溯删掉逗点} else break; // 不合法,直接结束本层循环}}// 判断字符串s在左闭右闭区间[start, end]所组成的数字是否合法bool isValid(const string& s, int start, int end) {if (start > end) {return false;}if (s[start] == '0' && start != end) { // 0开头的数字不合法return false;}int num = 0;for (int i = start; i <= end; i++) {if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法return false;}num = num * 10 + (s[i] - '0');if (num > 255) { // 如果大于255了不合法return false;}}return true;}
public:vector<string> restoreIpAddresses(string s) {result.clear();if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了backtracking(s, 0, 0);return result;}
};

78.子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

思路:子集问题是找树的所有节点

在这里插入图片描述

遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合。

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums, int startIndex) {result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己if (startIndex >= nums.size()) { // 终止条件可以不加return;}for (int i = startIndex; i < nums.size(); i++) {path.push_back(nums[i]);backtracking(nums, i + 1);path.pop_back();}}
public:vector<vector<int>> subsets(vector<int>& nums) {result.clear();path.clear();backtracking(nums, 0);return result;}
};

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

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

相关文章

Altium Designer(AD24)打开工程文件的几种方法

🏡《专栏目录》 目录 1,概述 2,源文件 2,菜单栏 4,工具栏 5,注意事项 1,概述 本文介绍几种打开工程文件的方法。 2,源文件 找到工程的源文件存储路径,找到.PrjPcb的源工程文件,双击打开。 2,菜单栏 第1步:执行File→Open, 第2步:找到工程文件的存储路径,并选中…

Linux嵌入式自学笔记(基于野火EBF6ULL):2.点灯与ubuntu安装

一、点灯登录root&#xff1a;账号&#xff1a;root ; 密码&#xff1a;root点灯命令&#xff1a;echo 0 > /sys/class/leds/red/brightness #关闭red灯 echo 0 > /sys/class/leds/blue/brightness #关闭blue灯 echo 0 > /sys/class/leds/green/brightness …

【Java实战㊷】Java实战:MyBatis-Plus 开启MySQL数据库高效操作之旅

目录 一、MyBatis-Plus 环境集成 1.1 项目依赖引入 1.2 数据库配置 1.3 代码生成器使用 二、核心 CRUD 操作实现 2.1 基础查询 2.2 数据新增与修改 2.3 复杂查询场景 三、性能优化与高级特性 3.1 缓存配置 3.2 乐观锁实现 3.3 字段自动填充 四、实战案例:用户管理模块开发 4.1…

开学季干货——知识梳理与经验分享

技术文章大纲&#xff1a;开学季干货——知识梳理与经验分享目标受众分析明确文章面向的学生群体&#xff08;如大学生、高中生&#xff09; 分析不同群体的核心需求&#xff08;课程准备、时间管理、工具使用&#xff09; 结合技术场景&#xff08;如数字笔记、在线协作&#…

Linux《线程(上)》

通过之前的学习我们已经了解了操作系统当中的基本的概念包括进程、基础IO、磁盘文件存储等&#xff0c;但是到目前为止我们还未了解到线程相关的概念&#xff0c;这就使得当前我们对操作系统的认知还不是完整的&#xff0c;现在我们是还是无法理解一个进程当中是如何同时的执行…

为什么知识复用时缺乏场景化指导影响实用性

知识复用时因缺乏场景化指导而严重影响实用性&#xff0c;其根本原因在于知识的价值本质上根植于其应用情境。脱离了场景的“纯知识”往往是抽象、片面且难以行动的。这导致了认知鸿沟的产生、隐性知识的流失、决策风险的增加、以及学习迁移效率的低下。当使用者面对一份缺乏“…

拥抱直觉与创造力:走进VibeCoding的新世界

引言 在传统观念里&#xff0c;编程是一项高度理性、逻辑严密的活动&#xff0c;开发者需要像建筑师一样&#xff0c;用代码一行行地精确构建数字世界。然而&#xff0c;随着人工智能技术的飞速发展&#xff0c;一种全新的编程理念和体验正在兴起——它就是 VibeCoding&#xf…

HTTP的Web服务测试在Python中的实现

在Web开发领域&#xff0c;对HTTP Web服务进行测试是确保服务稳定性和可靠性的关键步骤。Python作为一种功能强大的编程语言&#xff0c;提供了多种工具和库来简化这一过程。本文将介绍如何在Python中实现HTTP的Web服务测试。首先&#xff0c;Python的requests库是测试HTTP Web…

Android Studio 构建项目时 Gradle 下载失败的解决方案

一、问题原因分析根据错误日志&#xff1a;下载地址 https://services.gradle.org/distributions/gradle-8.1-bin.zip 连接超时&#xff08;10秒&#xff09;。可能原因&#xff1a;网络环境限制&#xff08;如公司防火墙、地区网络屏蔽&#xff09;。代理配置未生效或配置错误…

mysql 与 MongoDB 的分片

MySQL 和 MongoDB 作为不同类型数据库的代表(关系型 vs 文档型),其分片机制在设计理念、实现方式和适用场景上存在显著差异。两者的分片核心目标一致——通过水平扩展(Scale Out)解决单节点存储容量和性能瓶颈,但因数据模型、事务支持和分布式设计理念的不同,形成了截然…

Coze源码分析-资源库-创建知识库-前端源码-核心逻辑与接口

创建知识库逻辑 1. 表单验证系统 文件位置&#xff1a;frontend/packages/data/knowledge/knowledge-modal-base/src/create-knowledge-modal-v2/features/add-type-content/coze-knowledge/index.tsx 知识库创建表单的验证规则&#xff1a; // 知识库名称验证规则 const nameV…

欧拉函数 | 定义 / 性质 / 应用

注&#xff1a;本文为 “欧拉函数” 相关合辑。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 欧拉函数最全总结 jiet07 已于 2024-10-22 10:00:54 修改 一、欧拉函数的引入 首先引入互质关系&#xff1a; 如果两个正整数&#xff0c;除了 111 以…

ubuntu git push每次都要输入密码怎么解决只输入一次密码

在 Ubuntu 下使用 Git 时&#xff0c;如果每次 push 都需要重复输入密码&#xff0c;可以通过配置 Git 凭证存储来解决。以下是几种常用方法&#xff1a; &#x1f511; 方法一&#xff1a;使用 Git 凭证缓存&#xff08;推荐&#xff09; 设置凭证缓存&#xff08;默认 15 分钟…

【机械故障】使用fir滤波器实现数据拟合

使用fir滤波器实现数据拟合 提示&#xff1a;学习笔记 使用fir滤波器实现数据拟合使用fir滤波器实现数据拟合一、问题建模二、 构建矩阵方程&#xff08;关键步骤&#xff09;三、最小二乘解四、重要注意事项4.1 滤波器长度 M4.2 数据的预处理4.3 延迟问题4.4 性能评估一、问题…

STC8H系列-高级PWM-两相步进电机-细分驱动

两相步进电机, STC8H系列 用高级PWM实现SPWM细分驱动 /************* 功能说明 ************** 用B组高级PWM细分驱动2相4线小型步进电机, 支持1、2、4、8、16、32、64细分, 比如1.8度的电机4细分到0.45度. 本程序用于演示SPWM多细分直接驱动2相4线小型步进电机…

内网环境下ubuntu 20.04搭建深度学习环境总结

2025年9月更新&#xff0c;随着人工智能的发展&#xff0c;现在深度学习环境配置越来越简单了&#xff0c;常用的pytorch、paddle&#xff08;3.x&#xff09;等深度学习库安装的时候自带了cuda和cudnn的python包&#xff0c;不需要在操作系统层面自己安装&#xff0c;配置环境…

深入 Linux 文件系统:从数据存储到万物皆文件

深入 Linux 文件系统&#xff1a;从数据存储到万物皆文件 Linux 文件系统是一个精妙而复杂的工程&#xff0c;它像一座图书馆&#xff0c;不仅存放着书籍&#xff08;数据&#xff09;&#xff0c;还有一套高效的卡片索引系统&#xff08;元数据&#xff09;来管理它们。本文将…

C++, ffmpeg, libavcodec-RTSP拉流,opencv实时预览

文章目录RTSPStreamPlayer.cppRTSPStreamPlayer.hmain.cpp编译运行在ffmpeg_rtsp原有的rtsp拉流项目基础上加入了udp连接rtsp&#xff0c;日志模块&#xff0c;opencv实施预览等功能。RTSPStreamPlayer.cpp #include "RTSPStreamPlayer.h" #include <iostream>…

MySQL在Ubuntu 20.04 环境下的卸载与安装

目录 前言&#xff1a;学习引入 1、安装注意事项 2、学习建议 3、MySQL 和 MariaDB 核心概念一&#xff1a;它们是什么&#xff1f; 核心概念二&#xff1a;它们如何工作&#xff1f;&#xff08;“仓库”比喻&#xff09; 核心概念三&#xff1a;为什么它们如此流行&…

BizDevOps 是什么?如何建设企业 BizDevOps 体系

在数字经济加速渗透的今天&#xff0c;企业数字化转型已从 “技术升级” 转向 “价值重构”&#xff0c;单纯的 IT 研发或业务优化已难以适应市场快速变化。业务研发运营一体化&#xff08;BizDevOps&#xff09;作为打通 “业务 - 技术 - 运维” 协同壁垒的核心模式&#xff0…