[算法训练营] 回溯算法专题(一)

news2024/10/1 21:31:10

🕺作者: 主页

我的专栏
C语言从0到1
探秘C++
数据结构从0到1
探秘Linux
菜鸟刷题集

😘欢迎关注:👍点赞🙌收藏✍️留言

🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

文章目录

    • 前言
    • 77. 组合
      • 解题思路
      • AC代码
    • 216. 组合总和 III
      • 解题思路
      • AC代码
    • 39. 组合总和
      • 解题思路
      • AC代码
    • 40. 组合总和 II
      • 解题思路
      • AC代码
    • 131. 分割回文串
      • 解题思路
      • AC代码

前言

本篇为回溯算法专题的刷题题单,总共5道题,每道题都有对应的链接。

边听歌边刷题吧~

Thank You

推荐刷题路线 → 《代码随想录》

77. 组合

链接:77. 组合

解题思路

一开始我想的就是最暴力的算法,直接枚举所有情况,使用for循环这样,但是不同的数量的选择使得这个办法用不起来

然后看了一眼代码随想录的思路,回溯算法,在二叉树专题的时候写过,但是没想到怎么应用到这道题

回溯算法,可以把它想象成一棵二叉树,然后碰到节点就处理,处理后再撤销

在这题就可以使用path数组来存贮“路径”,然后最后满足情况就取,不满足就舍去

解题步骤:

  • 确定函数参数:n、k肯定是必要的,但是还需要一个参数来确定当前的递归遍历到哪个Index了

  • 确定终止条件:k的含义是“要求k个数的组合”,可以看作树的深度,当path数组的长度等于k时就说明数量取够了

  • 事件处理:从开始的index遍历,如果它小于n就可以将该数交给path,继续向下遍历,继续取数,函数的开始index需要往后挪一位,为了“尝试”其他的路径,在处理完后面的“事务”后就会把当前数字删掉去遍历其他的路径

AC代码

//未剪枝回溯算法
class Solution {
public:

    vector<vector<int>> res;
    vector<int> path;
    void backtraval(int n,int k,int startIndex){
        if(k==path.size()){
            res.push_back(path);
            return;
        }
        for(int i=startIndex;i<=n;++i){
            path.push_back(i);
            backtraval(n,k,i+1);
            path.pop_back();
        }
    }

    vector<vector<int>> combine(int n, int k) {
        res.clear();
        path.clear();
        backtraval(n,k,1);
        return res;
    }
};

//剪枝回溯算法
class Solution {
public:

    vector<vector<int>> res;
    vector<int> path;
    void backtraval(int n,int k,int startIndex){
        if(k==path.size()){
            res.push_back(path);
            return;
        }
        //path.size() : 现在path中的数量
        //k-path.size() : 还需要增加的数量
        //n - (k-path.size()) +1 : 在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历
        for(int i=startIndex;i <= n - (k-path.size()) +1 ;++i){
            path.push_back(i);
            backtraval(n,k,i+1);
            path.pop_back();
        }
    }

    vector<vector<int>> combine(int n, int k) {
        res.clear();
        path.clear();
        backtraval(n,k,1);
        return res;
    }
};

216. 组合总和 III

链接:216. 组合总和 III
在这里插入图片描述

解题思路

  • 和前面77题的思路类似,不过这个要求计算总和

  • 所以我直接用总和来减

  • 在终止条件中需要判断数的个数满足条件后,只要n==0就说明满足条件

AC代码

class Solution {
public:
    vector<vector<int>> com;
    vector<int> path;

    void travalback(int n,int k,int staticIndex){
        if(k==path.size()){
            if(n==0)com.push_back(path);
            return;
        }
        //当前剩余的数的和为n
        //如果开始的数大于剩下数的和,那么就没有意义了,所以for循环的条件就取min(9,n)
        //写成9也可以,但是一些本来就不满足条件的值就会重复进入循环
        for(int i=staticIndex;i<=min(9,n);++i){
            n-=i;
            path.push_back(i);
            travalback(n,k,i+1);
            path.pop_back();
            n+=i;
        }
    }
    vector<vector<int>> combinationSum3(int k, int n) {
        travalback(n,k,1);
        return com;
    }
};

39. 组合总和

链接:39. 组合总和

在这里插入图片描述

解题思路

  • 更详细的@代码随想录

  • 我的思路就是:它要组合就要遍历所有的选择,使用回溯算法加减数就可以了

  • 参数:原来的canducates 、target,肯定是需要的,还需要一个参数sum来记录总数和,一个参数staicIndex记录从数组的哪个位置开始

  • 终止条件:当总数=目标值时就可以停下来判断是否=目标值了

  • for循环:初始值就是staicIndex,条件是candicates的数组长度并且可以同时加上一个条件,当前的值加上总数是否≤目标值,如果是的话就继续,这样就不用多一次调用函数了

  • for循环内部:sum先加上candidates[i],path先将candidates[i]加入数组,再调用函数后再撤销

  • 注意candicates要先排序,这样就可以从最小的数开始

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;

    void travalBack(vector<int>& candidates, int& target ,int sum,int staicIndex){
        if(sum == target){
            res.push_back(path);
            return;
        }

        for(int i=staicIndex;i<candidates.size()&&sum + candidates[i] <= target;++i){
            sum+=candidates[i];
            path.push_back(candidates[i]);
            travalBack(candidates,target,sum,i);
            path.pop_back();
            sum-=candidates[i];
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        travalBack(candidates,target,0,0);
        return res;
    }
};

40. 组合总和 II

链接:40. 组合总和 II

在这里插入图片描述

解题思路

  • 依旧是推荐看@代码随想录

  • 以下是个人的思路

  • 首先可以知道的是最后的结果中不能含有重复的组合,如果要使用最后去重的方法的话很容易超时,所以只能在回溯算法中想办法

  • 显然当前的值如果和前面的值相等就说明肯定会有重复的组合

  • 怎么规避这样的情况呢?首先要说明的是我们应该是在for循环里处理这件事,因为我们是在for循环中选择当前的值的,其次还需要一个变量来记录前一个值是否被“取”过,我们用used[ ]来存储,如果是false,说明没有被“取”,此时就不能取当前的值,因为前一个值为当前节点的时候必然取过现在的当前节点,也就必然会重复。代码如下。

  • 还有这一句 travalBack(candidates,target-candidates[i],i+1,used);当进入它时,参数target就减去了candidates[i],但其实在现在函数的target并没有改变,也就实现了回溯的目的。

  • 很多话用语言很难表达,建议看看代码随想录的视频方便理解。

  • 剩下其他的其实没有说明好说的,和前面的回溯相关的题目一样

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void travalBack(vector<int>& candidates, int target, int staticIndex,vector<bool>&used)
    {
        if(target==0){
            res.push_back(path);
            return;
        }

        for(int i=staticIndex;i<candidates.size()&&target-candidates[i]>=0;++i)
        {
            if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)continue;
            path.push_back(candidates[i]);
            used[i]=true;
            travalBack(candidates,target-candidates[i],i+1,used);
            used[i]=false;
            path.pop_back();
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        if(candidates.size()==0)return res;
        vector<bool> used(candidates.size(),false);
        sort(candidates.begin(),candidates.end());
        travalBack(candidates,target,0,used);
        return res;
    }
};

131. 分割回文串

链接:131. 分割回文串

在这里插入图片描述

解题思路

一开始我老是想把判断是否是回文数的条件放在终止条件里面,但是发现不对劲却又说不上来,看了一眼题解@代码随想录,是我想的分割字符串的方式不对,它是要把满足回文数的子串保留,我那样的话没有分割,只是遍历了一次,做不到让子串的长度变化,而且还不能看到每一种情况。

总结如下:

  • 看到题目就要了解到可以使用回溯,但是难点在于如何分割子串

  • 如何分割子串?我们之前写回溯相关的题目时总会用到一个变量staticIndex,这是当我们需要一个参照物的时候,就需要这个变量来记录,然后随着for循环中i的增长,我们可选择的子串的长度就会增长,我们只需要判断从参照物staticIndex到i这一段的字符串是否满足回文数,如果满足就可以继续下一步,将这个子串保留,否则还是继续增长子串,在从处理下一步的函数中出来的时候,我们需要将path的最后的子串去掉,恢复现场去查询其他的子串。

AC代码

class Solution {
public:
    vector<vector<string>> res;
    vector<string> path;
    void travalback(string& s,int staticIndex)
    {
        if(staticIndex>=s.size())
        {
            res.push_back(path);
            return;
        }

        for(int i=staticIndex;i<s.size();++i)
        {
          //判断是否为回文数
            string p=s.substr(staticIndex,i-staticIndex+1);
            string q=p;
            reverse(p.begin(),p.end());
            if(p==q){
                path.push_back(p);
                travalback(s,i+1);//注意是i+1,取过不能再取
                path.pop_back();
            }else{
                continue;
            }
        }
    }
    vector<vector<string>> partition(string s) {
        if(s.size()==0)return res;
        travalback(s,0);
        return res;
    }
};

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

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

相关文章

Linux系列讲解 —— VIM配置与美化

目录 1. Vim基本配置1.1 配置文件1.2 基本配置 2. 插件管理器Vundle2.1 下载Vundle2.2 在vimrc中添加Vundle的配置 3. Vundle的使用3.1 安装插件3.2 卸载插件 1. Vim基本配置 1.1 配置文件 vim的配置文件有两处&#xff0c;请根据实际情况选择修改哪个。 (1) 全局配置文件&am…

电子元器件管理系统 JAVA语言开发

目录 一、系统介绍 二、系统下载 三、系统截图 一、系统介绍 基于VueSpringBootMySQL的电子元器件管理系统包含元器件单位模块、元器件仓库模块、元器供应商模块、元器件品类模块、元器件明细模块、元器件采购模块、元器件采购审核模块、元器件领用模块、学生元器件申请模块…

前端AJAX入门到实战,学习前端框架前必会的(ajax+node.js+webpack+git)(二)

阳光总在风雨后&#xff0c;请相信有彩虹。 案例 - 图书管理 bootstrap弹框 需求&#xff0c;点击添加按钮&#xff0c;没有离开当前页面&#xff0c;在当前页面弹出弹框&#xff08;弹窗&#xff09; 先学着实现一个简单的弹框&#xff0c;如下图右下角 bootstrap有两种方式…

openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户

文章目录 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户108.1 创建、修改和删除用户108.2 私有用户108.3 永久用户108.4 用户认证优先规则 openGauss学习笔记-108 openGauss 数据库管理-管理用户及权限-用户 使用CREATE USER和ALTER USER可以创建和管理数据…

解决虚拟机联网问题

虚拟机开机后发现右上角缺少联网标志(下面有正常联网标志)&#xff0c;这样就是连不上网的 不信你可以打开Ubuntu里面的浏览器或ping www.baidu.com 1.编辑虚拟机设置-->网络适配器-->如图所示 2.选择编辑-->虚拟网络编辑器 3.更改设置 4此处可以选择还原默认设置&am…

【JAVA学习笔记】42 - 内部类(难点,重点)

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter10/src/com/yinhai/innerclass_ 一、基本介绍 一个类的内部又完整的嵌套了另一个类结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是我们类的第五大成…

【蓝桥每日一题]-动态规划 (保姆级教程 篇12)#照相排列

这次是动态规划最后一期了&#xff0c;感谢大家一直以来的观看&#xff0c;以后就进入新的篇章了 目录 题目&#xff1a;照相排列 思路&#xff1a; 题目&#xff1a;照相排列 思路&#xff1a; 首先记录状态f[a][b][c][d][e]表示每排如此人数下对应的方案数&#xff0c;然…

java中按行读取文件内容

java中按行来读取文件内容&#xff0c;一般对文件也是又要求的&#xff0c;比如文件编码utf-8&#xff0c;内容是按行可读&#xff0c;而不是一堆字节码。这类文件&#xff0c;我们按行读取&#xff0c;主要是方便快速查看内容&#xff0c;并且用这些内容来作别的用途&#xff…

FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法

正常代码&#xff0c;部署到server 2012时&#xff0c;在上传音频mp3文件时&#xff0c;显示错误“不支持给定路径的格式”&#xff0c;上传控件使用FileUpload控件&#xff1a; 因为程序之前是正常的&#xff0c;因此应该不是程序的问题。 上传时&#xff0c;发现在选择文件时…

F28335-可移植新建工程模板-基于bitfield

文章目录 前言步骤新建工程工程管理拷贝底层文件 添加测试函数编写main.c测试函数 前言 实验要求利用28335芯片&#xff0c;重新学习一下DSP28335&#xff0c;并做个记录。 值得一提的是&#xff0c;28335只能用寄存器开发&#xff0c;而不能用库函数开发&#xff0c;相应的也…

LDAP注入漏洞

1、LDAP 注入 LDAP (Light Directory Access Portocol) 是基于X.500标准的轻量级目录访问协议&#xff0c;提供访问目录数据库方法的服务和协议&#xff0c;常用于与目录数据库组成目录服务。其中目录是一个为查询、浏览和搜索而优化的专业分布式数据库&#xff0c;它呈树状结…

Java学习 1.初识Java

1.类名 当这个类被public修饰时 class后的名字 类名必须和文件名相同 public class 类名(文件名); 2.main函数 方法一定是包含在类之中的 3.类里面是函数/方法 由返回值、方法名、参数列表组合而成,方法中可以定义变量&#xff0c;方法体 4.一个文件中可以有多个类 我们建…

【管理运筹学】第 10 章 | 排队论(3,标准的 M/M/1 排队系统)

文章目录 引言一、模型特征及分析二、系统指标1. 在系统中的平均顾客数&#xff08;队长的期望&#xff09;2. 在队列中的平均顾客数&#xff08;队列长的期望&#xff09;3. 在系统中顾客平均逗留时间4. 在队列中顾客的平均等待时间 写在最后 引言 前两篇文章&#xff0c;分别…

李沐——论文阅读——VIT(VIsionTransformer)

一、终极结论&#xff1a; 如果在足够多的数据上面去做预训练&#xff0c;那么&#xff0c;我们也可以不用 卷积神经网络&#xff0c;而是直接用 自然语言处理那边搬过来的 Transformer&#xff0c;也能够把视觉问题解决的很好 &#xff08;tips&#xff1a;paperswithcode.co…

【刷题-PTA】堆栈模拟队列(代码+动态图解)

【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2&#xff0c;请用这两个堆栈模拟出一个队列Q。 …

新华三路由器+华为交换机,实现华为交换机指定端口访问外网

需求背景&#xff1a; 多台服务器使用华为交换机组建了局域网&#xff0c;需要让交换机的指定端口可以访问外网。 需求分析&#xff1a; 交换机组建的局域网是二层组网&#xff0c;需借助路由器接入外网&#xff0c;然后通过DHCP分配内网IP地址给交换机指定端口连接的设备。 …

[资源推荐] 关于计算机毕设的方法论(重庆大学吕昱峰)

第一次刷到这个up主的视频是之前搜cpu设计的时候 视频链接&#xff1a;https://www.bilibili.com/video/BV1j7411P7gt?p1&vd_source0e8431ba6fd78bb2215c36307a75ac1a 最近学校毕设要开题了&#xff0c;但是感觉对毕业设计这个东西还是比较模糊&#xff0c;应该做到什么…

QML自定义电池状进度条

效果: 百分比显示保留两位小数,通过iValue的数值来显示当前进度,注意为了保留小数总值取的是10000,所以你的iValue值也要乘上100 变量说明: cBorderColor:进度条外框的颜色 cContentColor:表示进度的小方块颜色 cTextColor:显示进度百分比的文字颜色 iValue:当前进度,为整数(…

低代码软件的价格考量:成本效益与投资回报

数字化转型的今天&#xff0c;我们常听到“低代码”这个概念&#xff0c;那低代码软件价格到底如何呢&#xff1f;很多厂商并没有公布软件价格情况&#xff0c;让很多企业在采购的时候也是一头雾水。当然&#xff0c;市场上也存在一些厂商公开透明价格&#xff0c;比如Zoho Cre…

皮卡丘靶场——暴力破解

暴力破解 1. 基于表单的暴力破解 在登陆界面随便使用账号密码进行登录&#xff0c;使用bp抓包发送Intruder 我们需要破解账号&#xff08;username&#xff09;和密码&#xff08;password&#xff09;&#xff0c;就应当选择ClusterBomb&#xff08;集束炸弹&#xff09;的攻击…