力扣hot100-->递归/回溯

目录

递归/回溯

1. 17. 电话号码的字母组合

2. 22. 括号生成

3. 39. 组合总和

4. 46. 全排列

 5. 78. 子集


递归/回溯

1. 17. 电话号码的字母组合

中等

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

// 定义一个类 Solution
class Solution {
public:
    // 定义一个字符串向量 v,存储数字到字母的映射关系,数字 2 到 9 分别对应不同的字母组合
    vector<string> v{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
    
    // 定义一个字符串向量 result,用于存储所有可能的字母组合结果
    vector<string> result;
    
    // 定义一个字符串 path,用于记录当前组合的路径
    string path;

    // 递归函数 recursive,用于生成所有可能的字母组合
    void recursive(string digits, int index) {
        // 递归结束条件:当 path 的长度等于输入的数字串长度时,将当前组合添加到结果中
        if (path.size() == digits.size()) {
            result.push_back(path);
            return;
        }

        // 根据当前数字获取对应的字母字符串
        string s = v[digits[index] - '0'];

        // 遍历当前数字对应的每个字母
        for (int i = 0; i < s.size(); ++i) {
            // 将当前字母添加到 path
            path.push_back(s[i]);

            // 递归调用,下一个数字
            recursive(digits, index + 1);

            // 回溯:移除最后一个添加的字母,以便尝试下一个字母组合
            path.pop_back();
        }
    }

    // 主函数 letterCombinations,调用递归函数并返回结果
    vector<string> letterCombinations(string digits) {
        // 如果输入为空,返回空的结果集
        if (digits.size() == 0) return {};
        
        // 调用递归函数,开始生成字母组合
        recursive(digits, 0);

        // 返回所有生成的字母组合
        return result;
    }
};
 

2. 22. 括号生成

中等

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

提示:

  • 1 <= n <= 8

class Solution {
public:
    vector<string> ans;  // 存储所有有效的括号组合
    string cur;          // 当前构建的括号组合字符串

    // 回溯函数
    void backtrack(string& cur, int open, int close, int n) {
        // 基线条件:当当前组合的长度等于 2*n 时,意味着已生成一个有效的括号组合
        if (cur.size() == 2 * n) {
            ans.push_back(cur);  // 将当前组合添加到结果中
            return;              // 返回,结束当前递归
        }

        // 1. 尝试添加左括号
        if (open < n) {  // 只有在左括号数量小于 n 时才能添加左括号
            cur.push_back('(');  // 将左括号添加到当前组合
            backtrack(cur, open + 1, close, n);  // 递归调用,更新 open 数量
            cur.pop_back();  // 回溯:移除最后一个字符,尝试其他组合
        }

        // 2. 尝试添加右括号
        if (close < open) {  // 只有在右括号数量小于左括号数量时才能添加右括号
            cur.push_back(')');  // 将右括号添加到当前组合
            backtrack(cur, open, close + 1, n);  // 递归调用,更新 close 数量
            cur.pop_back();  // 回溯:移除最后一个字符,尝试其他组合
        }
    }

    // 生成 n 对括号的主函数
    vector<string> generateParenthesis(int n) {
        backtrack(cur, 0, 0, n);  // 初始调用,open 和 close 都为 0
        return ans;  // 返回所有生成的有效括号组合
    }
};

 解释:

  • 条件 if (close < open) 确保右括号的数量不会超过左括号的数量,从而保持组合的有效性。

3. 39. 组合总和

中等

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5],target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

class Solution {
public:
    // 用于存储所有符合条件的组合
    vector<vector<int>> result;
    // 用于存储当前的组合路径
    vector<int> path;

    // 递归+回溯函数
    // candidates: 数组中的可选数字
    // target: 目标和
    // sum: 当前路径的和
    // index: 当前选择开始的索引位置
    void backTracking(vector<int>& candidates, int target, int sum, int index) {
        // 如果当前和已经超过目标值,结束该路径
        if(sum > target){
            return;
        }

        // 如果找到一个符合条件的组合
        if(target == sum){
            result.push_back(path);  // 将当前路径加入结果集中
            return;
        }

        // 遍历候选数组,从 index 开始
        for(int i = index; i < candidates.size(); ++i) {
            // 将当前选择加入路径和 sum 中
            sum += candidates[i];
            path.push_back(candidates[i]);

            // 递归调用自身,传入i而不是index或index+1,以允许重复使用相同的元素
            backTracking(candidates, target, sum, i);

            // 回溯,撤销当前选择
            sum -= candidates[i];
            path.pop_back();
        }
    }

    // 主函数,用于调用回溯函数并返回结果
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backTracking(candidates, target, 0, 0); // 初始化 sum 为 0,从索引 0 开始
        return result;  // 返回符合条件的所有组合
    }
};

 解释:

关于for循环中int=index而不是i=0问题:

考虑一个具体的例子,假设我们有以下输入:

candidates = [2, 3, 6, 7] target = 7

1. 使用 index 控制

index 传入 0,第一次迭代将会是:

  • 第一个元素 2index = 0
    • 选择 2,接下来可以再次选择 2 或选择 3(因为 i0 开始)。你将得到 [2, 2, 3] 这种组合。
  • 第二个元素 3index = 1
    • 当递归调用回来后,当前元素 2 被弹出,index 继续向前推进。
    • 选择 3 时,i1 开始,因此可以选择 [3, 4],形成组合 [3, 4]

这个方法避免了相同元素在同一层级的选择,比如 2 被重复选择。

2. 使用 0 开始的情况

如果 for 循环从 0 开始:

  • 第一个元素 2i = 0
    • 选择 2,然后 backTracking(candidates, target, sum, 0) 再次递归调用,允许再次选择 2
  • 第二个元素 2 再次选择i = 0
    • 你将得到路径 [2, 2, 2],但也会通过选择 2、然后 2、然后 3 再生成组合。
造成的后果

通过从 0 开始,每一层递归都允许选择之前已经被选中的元素,导致生成的组合可能重复。例如 [2, 2, 3][2, 3, 2] 被认为是不同的组合,但实际上它们是相同的组合。

总结

  • index 变量的设计:允许算法在递归过程中控制哪些元素可以被选择,同时避免在同一递归层级重复选择已经在路径中的元素。
  • 0 开始的后果:如果每次递归都从 0 开始,可能会产生重复组合,并增加计算的复杂度。

4. 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 中的所有整数 互不相同

class Solution {
public:
    // 存储所有的排列结果
    vector<vector<int>> result;
    // 存储当前排列的路径
    vector<int> path;

    // 回溯函数
    void backTracking(vector<int>& nums, vector<bool>& used) {
        // 终止条件:当前路径的长度等于数组的长度,表示形成了一个完整排列
        if (nums.size() == path.size()) {
            result.push_back(path);  // 将当前排列加入结果集
            return;
        }

        // 遍历所有元素,尝试每种可能
        for (int i = 0; i < nums.size(); ++i) {
            // 如果该元素已经使用过,则跳过
            if (used[i]) continue;

            // 标记当前元素为已使用
            used[i] = true;
            path.push_back(nums[i]);  // 将元素加入当前排列路径
            
            // 递归调用自身,继续生成排列
            backTracking(nums, used);

            // 回溯,撤销当前选择,准备尝试其他排列
            path.pop_back();
            used[i] = false;
        }
    }

    // 主函数,返回所有的排列
    vector<vector<int>> permute(vector<int>& nums) {
        // 初始化标记数组,长度和 nums 一致,初始值为 false
        vector<bool> used(nums.size(), false);
        
        // 开始回溯
        backTracking(nums, used);
        return result;
    }
};

5. 78. 子集

中等

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

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

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

class Solution {
public:
    vector<vector<int>> result; // 用于存储所有子集的结果
    vector<int> path; // 当前的子集路径

    void backtrack(vector<int>& nums, int index) {
        // 结束条件:当 index 等于数组大小时返回
        if (index == nums.size()) {
            return;
        }

        // 将当前子集加入结果中
        result.push_back(path);

        // 遍历剩余的元素,生成子集
        for (int i = index; i < nums.size(); ++i) {
            path.push_back(nums[i]); // 选择当前元素
            backtrack(nums, i + 1); // 递归调用,选择下一个元素
            path.pop_back(); // 回溯,撤销选择
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        backtrack(nums, 0); // 从索引 0 开始
        return result; // 返回结果
    }
};

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

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

相关文章

[MySQL#4] 表约束(1) | NULL | default | zerofill | 主键 | 自增长

目录 1. 表约束概述 2. 空属性&#xff08;null/not null&#xff09; 3. 默认值&#xff08;default&#xff09; 4. 列描述&#xff08;comment&#xff09; 5. zerofill 6. 主键&#xff08;primary key&#xff09; 7. 自增长&#xff08;auto_increment&#xff09…

Android中常用adb命令

目录 1.adb连接安卓模拟器 2.adb列出所有已经连接的设备 3.adb显示设备的日志信息 4.adb 电脑文件推送到安卓模拟器中 5.adb 手机传送文件到电脑 6.adb获取安卓应用的包名和Activity名 附录 1--命令 1&#xff09;adb devices 2&#xff09;adb install 路径> 3&#xff09;…

【项目管理】PMP冲刺真题200题 (题目+解析)乱序版 【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

尚硅谷 | Nginx | 学习笔记

尚硅谷 | Nginx | 学习笔记 尚硅谷Nginx教程由浅入深&#xff08;一套打通丨初学者也可掌握&#xff09;_哔哩哔哩_bilibili 文章目录 尚硅谷 | Nginx | 学习笔记一、Nginx相关概念1.Nginx是什么2.正向代理和反向代理正向代理反向代理 3.负载均衡和动静分离负载均衡动静分离 二…

小米迎来「新起点」:硬核创新从超越到引领,小米SU7 Ultra 发布

发布 | 大力财经 10月29日&#xff0c;小米15系列暨小米澎湃OS 2新品发布会在北京召开&#xff0c;小米集团创始人、董事长兼CEO雷军宣布了小米汽车原型车在纽北跑出6分46秒874的圈速&#xff0c;登顶“纽北全球最速四门车”的好消息&#xff0c;并领衔发布了小米15系列手机、…

若依微服务架构遇到的一些问题记录

一、nacos启动问题 需要看官网的准备工作&#xff0c;认真看&#xff0c;版本问题卡了两天 https://doc.ruoyi.vip/ruoyi-cloud/document/hjbs.html#%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C 1.下载nacos&#xff0c;版本需要对应上 版本说明链接 2.记得运行数据库&#xff0…

【工具】Charles对360浏览器抓包抓包

Charles 和 switchy sharp 配合&#xff0c;可以对 Chrome 进行抓包也可以配合对360安全浏览器抓包。 本文以Windows 电脑中的配置为例&#xff0c;介绍如何实现抓包。&#xff08;Mac中操作基本一致&#xff09; 1.安装Charles 可根据自己的电脑下载对应的版本&#xff1a;…

小小猫棒onu替换家用光猫,薅运营商带宽羊毛,突破1000M

小小猫棒onu 一、总体步骤 1 记录原来光猫信息 主要包括SN&#xff0c;ploam密码&#xff0c;loid、loid密码、 mac、上网的vlan id等 一般gpon采用SN、ploam密码、SNploam密码三种中的一种认证方式 一般Epon采用loid&#xff08;逻辑id&#xff09;、mac、loid mac三种中…

Kafka-代码示例

一、构建开发环境 File > New > Project 选择一个最简单的模板 项目和坐标命名 配置maven路径 添加maven依赖 <dependencies><!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients --><dependency><groupId>org.apache.kaf…

vue2项目在发布后更新,提示用户刷新页面

1、在项目根目录创建resetVersion.js的文件&#xff0c;内容如下 &#xff08;具体路径可能会有点问题&#xff0c;但是不影响&#xff09; const path require(path); const fsExtra require(fs-extra);const runBuild async () > {try {const OUTPUT_DIR public; // …

WebGIS开发丨从入门到进阶,全系列课程分享

WebGIS开发所需的技能 1.前端技能&#xff1a;Html、CSS、 Javascript、WebAPLs、Vue 2.二维技能&#xff1a;WebGIS基础理论及开发、MapGIS二次开发Openlayers、Leaflet、Mapbox 、Echarts、公共开发平台开发等 3.三维技能&#xff1a;Blender、Three.js、Cesium等 Web开发…

17 Docker容器存储架构:docker存储持久化-bind mount

文章目录 三、docker存储持久化-bind mount3.1 将 /root/htdocs 目录下的 index.html 文件挂载给一个 httpd 容器3.2 更新宿主机上的 index.html 文件内容,并查看容器中的内容3.3 查看挂载类型3.4 创建基于 docker volume 的 container 镜像3.5 删除容器,销毁容器后,volume 依…

centos7 zabbix监控nginx的pv和uv和status_code

zabbix监控nginx的pv&#xff1a; pv)cat /var/log/nginx/access.log|awk {print $1}|wc -l;;zabbix-get验证&#xff1a; [rootbogon ~]# zabbix_get -s 192.168.253.231 -k pv_uv[pv] 100zabbix监控nginx的uv uv)cat /var/log/nginx/access.log|awk {print $1}|uniq -c | w…

C++进阶-->多态(Polymorphism)

1. 多态的概念 多态&#xff0c;顾名思义多种形态&#xff1b;多态分为编译时多态&#xff08;静态多态&#xff09;和运行时多态&#xff08;动态多态&#xff09;&#xff0c;静态多态就是就是我们前面讲的函数重载和函数模板&#xff0c;可以通过传不同类型&#xff0c;然后…

虚拟机桥接模式连不上,无法进行SSH等远程操作

说明&#xff1a;以下情况在window10上遇到&#xff0c;解决后顺便做了个笔记&#xff0c;以防后续再次用到&#xff0c;也给同道中人提供一个解决方案 一、首先按照以下步骤进行检查 1、是否连接了对应的wifi 2、是否设置了桥接模式 3、上述1、2确认无误的情况下请查看右上…

Flutter Image和Text图文组件实战案例

In this section, we’ll go through the process of building a user interface that showcases a product using the Text and Image widgets. We’ll follow Flutter’s best practices to ensure a clean and effective UI structure. 在本节中&#xff0c;我们将使用“Te…

使用Kubernetes管理容器化应用

使用Kubernetes管理容器化应用 Kubernetes简介 安装Kubernetes 安装Minikube 启动Minikube集群 创建一个简单的Web应用 创建项目目录 初始化项目 安装Node.js依赖 创建Docker镜像 编写Dockerfile 构建并推送Docker镜像 创建Kubernetes配置文件 创建Deployment 创建Service …

<十六>Ceph mon 运维

Ceph 集群有故障了&#xff0c;你执行的第一个运维命令是什么&#xff1f; 我猜测是ceph -s 。无论执行的第一个命令是什么&#xff0c;都肯定是先检查Mon。 在开始之前我们有必要介绍下Paxos协议&#xff0c;毕竟Mon就是靠它来实现数据唯一性。 一&#xff1a; Paxos 协议 1…

计算机网络-MSTP的基础概念

前面我们大致了解了MSTP的由来&#xff0c;是为了解决STP/RSTP只有一根生成树导致的VLAN流量负载分担与次优路径问题&#xff0c;了解MSTP采用实例映射VLAN的方式实现多实例生成树&#xff0c;MSTP有很多的理论概念需要知道&#xff0c;其实与其它的知识一样理论复杂配置还好的…

电源完整性

电源分配系统 电源分配系统:Power Distribution Network(简称 PDN) 真正用电节点在 Die&#xff0c;所以PDN系统包含 PCB 和 Package上的部分 PCB 上:VRM、大电容、小电容、电源平面、地平面 Package内:电容、电源平面、地平面 电源噪声的产生 稳压电源芯片本身的输出不恒定&a…