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

简介: [算法训练营] 回溯算法专题(二)

前言

本篇共5题,附有解题思路和AC代码。

边听歌边刷起来吧~

🎸Make Believe

93. 复原 IP 地址

链接:93. 复原 IP 地址

解题思路

  1. travalBack 函数是递归函数,用于遍历字符串 s 中可能的 IP 地址组合。它有三个参数:staticIndec 表示当前小节的起始位置,dotCount 表示已经添加的逗号数量。
  2. 当 dotCount 等于 3 时,说明已经添加了三个逗号,此时可以判断第三个逗号后面的字符组成的字符串是否满足 IP 地址的要求。如果满足,则将该情况存入 res 中。
  3. 在 travalBack 函数中使用循环来遍历从 staticIndec 开始到字符串末尾的位置。对于每一个位置 i,判断从 staticIndec 到 i 组成的小节是否满足 IP 地址的要求。
  4. 如果满足条件,则在 s 中插入逗号,并递归调用 travalBack 函数,同时更新 staticIndec 为 i+2,dotCount 加 1,表示处理下一个小节。
  5. 在递归调用结束后,需要将插入的逗号删除,以便进行下一次循环。
  6. isIPmember 函数用于判断一个字符串是否满足 IP 地址的要求。它将字符串转换为整数,并进行一系列的判断条件,包括长度不为 1 且以 0 开头、只包含数字字符等。
  7. 在 restoreIpAddresses 函数中,首先对输入字符串 s 的长度进行判断,如果小于 4 或大于 12,则直接返回空结果。然后调用 travalBack 函数开始递归遍历。
  8. 最终返回存储所有满足条件的 IP 地址组合的 res。

AC代码

class Solution {
public:
    vector<string> res;
    //staticIndec是这小节的开始,dotCount是添加逗号的数量
    void travalBack(string& s,int staticIndec,int dotCount)
    {
        if(dotCount==3){//如果添加的逗号的数量等于3了就可以判断第三个逗号后面的字符组成的字符串是否满足条件,满足就把这一种情况存到res中。
            if(isIPmember(s,staticIndec,s.size()-1))res.push_back(s);
            return;
        }
        for(int i=staticIndec;i<s.size();++i){
            if(isIPmember(s,staticIndec,i)){//判断这一节是否满足条件
                s.insert(s.begin() + i + 1 , '.');//向s中插入逗号"."
                travalBack(s,i+2,dotCount+1);//因为插入了逗号,所以staticIndec要多加1
                s.erase(s.begin() + i + 1);
            }
            else break;
        } 
    }
    bool isIPmember(string& s,int l,int r){
        string str = s.substr(l,r-l+1);
        if(str.size()!=1 && str[0]=='0')return false;
        for(int i=0;i<str.size();++i){
            if(str[i]>'9'||str[i]<'0'){
                return false;
            }
        }
        if(str=="")return false;
        if(stoll(str)>255)return false;
        return true;
    }
    vector<string> restoreIpAddresses(string s) {
        if(s.size()<4||s.size()>12)return res;
        travalBack(s,0,0);
        return res;
    }
};

78. 子集

链接:78. 子集

解题思路

  • 终止条件:不需要写出来因为当staticIndex>nums.size()时,循环已经终止了
  • for循环内:在进入下一个节点前先将自己这个节点载入path中,出来时就删去
  • 可以看作是二叉树的前序遍历

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void travalBack(vector<int>& nums,int staticIndex)
    {
        res.push_back(path);
        for(int i=staticIndex;i<nums.size();++i)
        {
            path.push_back(nums[i]);
            travalBack(nums,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        travalBack(nums,0);
        return res;
    }
};

90. 子集 II

链接:90. 子集 II

解题思路

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void travalBack(vector<int>& nums,int staticIndex,vector<bool> used)
    {
        res.push_back(path);
        for(int i=staticIndex;i<nums.size();++i)
        {
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false)
            {
                continue;
            }
            else{
                path.push_back(nums[i]);
                used[i]=true;
                travalBack(nums,i+1,used);
                used[i]=false;
                path.pop_back();
            }
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<bool> used(nums.size(),false);
        travalBack(nums,0,used);
        return res;
    }
};

491. 递增子序列

链接:491. 递增子序列

解题思路

  • 终止条件:当path长度大于2时都可以保留,但是和之前不同在于不需要return,如果return会导致只有长度为2的path
  • 这题需要去重但是不能使用排序,因为题目要求是原数组的递增子序列
  • 在判断是否能够加入当前数字时的条件是:
  • 如果当前的path不为空,那么前一个值必须比后一个值小,否则跳过
  • 还有就是当前的值是否“用过”,这里需要注意一个点,在for循环之前要使用一个set来记录元素是否使用过,如果使用过就要跳过,避免重复

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void travalBack(vector<int>& nums,int staticIndex)
    {
        if(path.size()>=2){
            res.push_back(path);
        }
        unordered_set<int> umap;
        for(int i=staticIndex;i<nums.size();++i)
        {
            if((!path.empty()&&path.back()>nums[i])||umap.find(nums[i])!=umap.end())
            {
                continue;
            }
            else{
                path.push_back(nums[i]);
                umap.insert(nums[i]);
                travalBack(nums,i+1);
                path.pop_back();
            }
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        travalBack(nums,0);
        return res;
    }
};

46. 全排列

链接:46. 全排列

解题思路

  1. 首先,定义了一个全局变量res,用于存储最终的结果,即全排列的集合。
  2. 定义了一个全局变量path,用于存储当前正在生成的一个排列。
  3. 定义了一个辅助函数travalBack,用于进行回溯遍历生成全排列。
  4. 在travalBack函数中,首先判断当前path的长度是否等于给定数组nums的长度,如果相等,说明已经生成了一个完整的排列,将其加入到res中,并返回。
  5. 如果path的长度不等于nums的长度,就需要继续生成排列。通过一个循环遍历nums数组的每一个元素。
  6. 在循环中,首先判断当前元素是否已经被使用过,即used[i]是否为true,如果是,则跳过当前元素,继续下一次循环。
  7. 如果当前元素没有被使用过,将其加入到path中,将used[i]设置为true表示该元素已经被使用。
  8. 然后,递归调用travalBack函数,继续生成下一个元素的排列。
  9. 在递归调用结束后,需要将used[i]重新设置为false,表示该元素未被使用。
  10. 同时,还需要将path中的最后一个元素移除,以便生成下一个排列。
  11. 在permute函数中,首先创建一个大小为nums长度的布尔型数组used,初始值都为false。
  12. 然后调用travalBack函数,开始生成全排列。
  13. 最后返回得到的全排列结果res。

AC代码

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    //排列和组合不同,它在乎顺序和单次,所以要使用一个数组used来记录是否使用过
    void travalBack(vector<int>&nums ,vector<bool> used)
    {
        //判断是否是全排列,即包含所有元素
        if(path.size()==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();++i){
            //used[i]等于true说明已经使用1过这个数了,就要跳过
            if(used[i]==true)continue;
            path.push_back(nums[i]);
            used[i]=true;
            travalBack(nums,used);
            used[i]=false;
            path.pop_back();
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        travalBack(nums,used);
        return res;
    }
};


相关文章
|
9天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
9天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
9天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之顺序表【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找等具体详解步骤以及举例说明
|
8天前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
9天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
9天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
9天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
9天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之顺序表习题精讲【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
顺序表的定义和基本操作之插入;删除;按值查找;按位查找习题精讲等具体详解步骤以及举例说明
|
9天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习【无论是王道考研人还真爱粉都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构的基本概念;算法的基本概念、特性以及时间复杂度、空间复杂度等举例说明;【含常见的报错问题及其对应的解决方法】
|
9天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!