IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 回溯算法刷题 -> 正文阅读

[数据结构与算法]回溯算法刷题

回溯算法集合


回溯算法也有很多题目,全排列什么的,这些都是难者不会会者不难的题。

我一开始就一道也做不出来,后来就感觉其实还可以。也弄个记录,希望学有所得,练有所获。

回溯算法简介-个人理解

个人感觉,回溯法就是枚举加上剪枝,一般是要求出所有的组合,排列这种的,给你一个集合。让从里面取元素出来符合某一个目标。一个集合要是有n个元素,那么一共可能的组合数是很大的,这个不同的要求不同,比如有无顺序要求,可不可以重复取出等等,具体问题具体分析。但是大多数情况,我们暴力枚举的话肯定是时间复杂度太大了。

回溯呢就是我们在遍历的过程中,到了某一个位置,我们可以判断出是否到达目标,或者下面还有没有可能到达目标了。

如果是还有机会的话,就继续向下延伸,但是要是判断已经到头了,就及时止损,回退一步,换个方向探索。这样的话就可以避免很多不需要的搜索,提高解决问题的效率。

回溯法一般都是树状的搜索过程,而树结构天然就和递归结果非常的契合,所以回溯算法一般使用递归形式,好理解也好书写,但是具体问题如果有性能要求,到时看看能不能改写为迭代的,省内存。

leetcode-39. 组合总和🌟🌟

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

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

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

解题思路

这个问题是让求出可能的组合数,也不需要是什么连续子数组这种,算是比较好弄的。

代码实现

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>>vv; //用来存放最后的结果
        vector<int>v; 
        dfs(candidates,target,v,vv);
        return vv;
    }
    void dfs(vector<int>& candidates, int target,vector<int>&v,vector<vector<int>>&vv){
        if(target<0)return;
        if(target==0){ //判断出找到了一个可行的组合
            vv.push_back(v);
            return ;
        }
        for(int i=0;i<candidates.size();i++){
          //
            if(v.size()>0&&v.back()<=candidates[i]||v.size()==0){
                v.push_back(candidates[i]); //满足条件 填入组合
                dfs(candidates,target-candidates[i],v,vv); //在这个组合基础上进行递归,相当于到了一个新的分支
                v.pop_back();//退一步去探寻其他分支
            }
        }
    }
};

leetcode-17. 电话号码的字母组合🌟🌟

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zr1qS4ty-1652626890378)(/Users/autreyhepburn/Library/Application Support/typora-user-images/image-20211212234617933.png)]

解题思路

这就是列举可能的结果,我觉得回溯重要就是要想清楚到哪里要停,这题也是比较好想到的。看代码就懂了

代码实现

class Solution {
public:
    string num[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> letterCombinations(string digits) {
        //用来存放数字
        vector<string> vs;//用来存放结果
        int n=digits.size();
        if(n==0)return vs;
        string s="";//用来记录可行的结果
        huishu(0,n,s,vs,digits);
        return vs;

    }
  //k是用来记录当前取了几个数字 n是数字总数
    void huishu(int k,int n,string &s,vector<string> &vs,string digits){
        if(k==n){//取到n个数了可以返回了
            vs.push_back(s);
        }else{
          //这是回溯的主要实现 用循环遍历所有可取的字符
            for(int i=0;i<num[digits[k]-'0'].size();i++){
              //先push 然后递归向下,递归结束再 pop相当于回退一步 
                s.push_back(num[digits[k]-'0'][i]);
                huishu(k+1,n,s,vs,digits);
                s.pop_back();
            }
        }
    }

};

leetcode-46. 全排列🌟🌟

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

解题思路

这是最经典的一个回溯了吧,就是列出所有的可能,其实就是枚举,但是回溯的树形最适合这种枚举了,不用考虑重复情况。全排列不重复,要是组合的话要考虑重复了。

代码实现

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> vv;//用来存放结果
        vector<int>v;//存放可行的排列
        int n=nums.size();//排列的长度要知道
        vector<int>visit(n,0); //这个是排列,排列里面不能有重复的
        dfs(n,v,vv,nums,visit);
        return vv;
    }
    void dfs(int n,vector<int>&v,vector<vector<int>> &vv,vector<int> nums,vector<int> &visit){
        if(v.size()==n){
            vv.push_back(v);
        }else{
            for(int i=0;i<nums.size();i++){
                if(visit[i]==0){
                    visit[i]=1;
                    v.push_back(nums[i]);
                    dfs(n,v,vv,nums,visit);
                    v.pop_back();
                    visit[i]=0;
                }
            }
        }
    }
};

leetcode-77. 组合🌟🌟

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

解题思路

排列完就是组合了,就是不能考虑顺序了,那就从前往后就可以了,就是控制好只能取1,3不能取3,1就好了。

代码实现

在排列的基础上小改一下,首先这个是从前往后取的,所以不存在返回去,就不需要visit数组了,其次这个可以减枝,就是要是剩下没有访问到的加上当前的还不够k,那就没必要往下走了

class Solution {
public:
    vector<vector<int>> combine(int n,int k) {
        vector<vector<int>> vv;//用来存放结果
        vector<int>v;//存放可行的组合
        
        dfs(n,k,1,v,vv);
        return vv;
    }
    void dfs(int n,int k,int m,vector<int>&v,vector<vector<int>> &vv){
        //回溯需要减枝啊还是
        if (v.size() + (n - m + 1) < k) {
            return;
        }
        if(v.size()==k){
            vv.push_back(v);
        }else{
            for(int i=m;i<=n;i++){
                    v.push_back(i);
                    dfs(n,k,i+1,v,vv);
                    v.pop_back();
                  
            }
        }
    }
};

leetcode-37. 解数独🌟🌟🌟

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

image-20211213133254208

解题思路

这个的思路就是枚举加减枝啊,每次到了一个空的地方就得枚举这个地方可以填的数字,取出一个数字以后要判断他可不可以,如果可以的话就去处理下一空格,如果当前这一个分支是不可行的,就要回退

这个过程通常都是通过在递归前后改变状态完成的。

image-20211213133547700

例如上图这个,dfs调用前设置为1,在调用后设置为0,代表回退。

这道题的给我的一个启发是,之后这种N*N处理空格的,可以吧空格的坐标用pair<int,int>坐记录,这样在处理过程中不需要再遍历了。这个我一开始没有想到。

这个处理的结尾就是判断所有需要处理的位置是否都进行了处理。

image-20211213133819254

这行代码代码就是做这个工作的。回溯算法一般是有一个框架的,我们要想清楚该怎么忘框架里面填东西。

代码实现

class Solution {
public:
    bool vr[9][9];
    bool vc[9][9];
    bool vb[3][3][9];
    vector<pair<int,int>> mspace;
    void solveSudoku(vector<vector<char>>& board) {
        //要先初始化一下
        memset(vr, false, sizeof(vr));
        memset(vc, false, sizeof(vc));
        memset(vb, false, sizeof(vb));
         for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                if (board[i][j] == '.') {
                    mspace.emplace_back(i, j);
                }
                else {
                    int digit = board[i][j] - '0' - 1;
                    vr[i][digit] = vc[j][digit] = vb[i / 3][j / 3][digit] = true;  
                }
               
            }
          
        }
            dfs(0,board);
                  
    }
    bool dfs(int index,vector<vector<char>>& board){
        if(index==mspace.size())return true;
        int m=mspace[index].first;
        int n=mspace[index].second;
        for(int i=0;i<9;i++){
            if(vr[m][i]==false&&vc[n][i]==false&&vb[m/3][n/3][i]==false){
                vr[m][i]=1;
                vc[n][i]=1;
                vb[m/3][n/3][i]=1;
                board[m][n]='0'+i+1;
                if(dfs(index+1,board)){
                    return true;
                }
                vr[m][i]=0;
                vc[n][i]=0;
                vb[m/3][n/3][i]=0;
            }
        }
        return false;

    }
};
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-05-19 12:02:44  更:2022-05-19 12:03:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:37:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码