LeetCode刷题——回溯法

news2024/11/6 3:05:38

文章目录

  • [中等]全排列
  • [中等]全排列 II
  • [中等]组合总和
  • [中等]组合总和 II
  • [中等]复原 IP 地址
  • [中等]括号生成
  • [中等]子集
  • [中等]单词搜索
  • [中等]组合

[中等]全排列

  • 原题链接
  • 题解
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> num;
    int flag[10];
    vector<vector<int>> permute(vector<int>& nums) {
        recall(nums);
        return ans;
    }
    void recall(vector<int>& nums) {
        if (num.size() == nums.size()) {
            ans.push_back(num);
        }
        for (int i = 0; i < nums.size(); i++) {
            if (flag[i] == 0) {
                flag[i] = 1;
                num.push_back(nums[i]);
                recall(nums);
                flag[i] = 0;
                num.pop_back();
            }
        }   
    }
};

[中等]全排列 II

  • 原题链接
  • 题解
    在全排列问题的回溯法题解基础上,加上剪枝的思路,先对nums中的数字进行排列,则重复的数字会靠在一起,例如2,2,2,2,保证他们按照固定的顺序被选中,则判断思路是,如果前一个数字与当前数字相同,且前一个数字还没有被选,则排除这个分支。也就是使用continue语句跳过目前这个分支
vector<vector<int>> ans;
    vector<int> num;
    int flag[10];
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        recall(nums);
        return ans;
    }
    void recall(vector<int>& nums) {
        if (num.size() == nums.size()) {
            ans.push_back(num);
        }
        for (int i = 0; i < nums.size(); i++) {
            if (flag[i] == 0) {//这个数还没取过
                if (i-1>=0) //存在前一个数
                    if (flag[i-1]==0 && nums[i-1]==nums[i])//前一个数与当前数相同且还没取过
                        continue;
                flag[i] = 1;
                num.push_back(nums[i]);
                recall(nums);
                flag[i] = 0;
                num.pop_back();
            }
        }
    }

[中等]组合总和

  • 原题链接
  • 题解
    涉及剪枝问题,其实动手画一下回溯路径就比较好理解了
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> num;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        recall(candidates,0,target,0);
        return ans;
    }
    void recall(vector<int>& nums,int sum,int target, int k) {
        if (sum == target) {
            ans.push_back(num);
        }
        else if(sum > target){
            return;
        }
        for (int i = k; i < nums.size(); i++) {
            num.push_back(nums[i]);
            sum += nums[i];
            recall(nums,sum,target,i);
            sum -= nums[i];
            num.pop_back();
        }
    }
};

[中等]组合总和 II

  • 原题链接
  • 题解
    就是前面的全排列 II组合总和两个问题的剪枝思路的合并,给传入的参数排序之后
    1.对相同的数字,要按照原先的顺序选取
    2.限制同组合的反复选取,比如[1,2,5] 在第一个分支中就取到,之后取了2之后就不在考虑取1,因为选取元素[1,2]与选取元素[2,1]从集合角度考虑并无不同。
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> num;
    int flag[110];
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        recall(candidates,target,0,0);
        return ans;
    }
    void recall(vector<int>& nums, int target, int sum, int k) {
        if (sum == target) {
            ans.push_back(num);
        }
        else if(sum>target){
            return;
        }
        for (int i = k; i < nums.size(); i++) {
            if (flag[i] == 0) {//这个数还没取过
                if (i-1>=0) //存在前一个数
                    if (flag[i-1]==0 && nums[i-1]==nums[i])//前一个数与当前数相同且还没取过
                        continue;
                flag[i] = 1;
                sum += nums[i];
                num.push_back(nums[i]);
                recall(nums,target,sum,i);
                flag[i] = 0;
                sum -= nums[i];
                num.pop_back();
            }
        }
    }
};

[中等]复原 IP 地址

  • 原题链接
  • 题解
    这题卡了很久
    ①字符串的操作要熟练一点,包括substr(),earse(),insert()的用法
    ②在回溯法的循环中记得要加上判断i < s.size() && i < f + 3:每一个子段最多3位,并且不能越界
class Solution {
public:
    vector<string> ans;
    vector<string> restoreIpAddresses(string s) {
        string str = "";
        recall(s, 0, 0, str);//从第一段开始回溯;
        return ans;
    }

    //回溯方法,递归调用
    //f表示现在遍历到数组的第几个元素
    //k表示当前的层数,因为ip地址就分四段,分到第四段就需要进行判断
    //str表示一组答案
    void recall(string s, int f, int k, string str) {
        if (k == 4) {
            if (f != s.size()) return;
            str.erase(str.size() - 1, 1);//去掉.
            ans.push_back(str);
            str.insert(str.size(), 1, '.');//把.加回去,保留现场
            return;
        }
        else if (f == s.size()) return;
        for (int i = f; i < s.size() && i < f + 3; i++) {
            if (lawful(s, f, i)) {
                str.insert(str.size(), s, f, i - f + 1);
                str.insert(str.size(), 1, '.');
                recall(s, i + 1, k + 1, str);
                str.erase(str.size() - (i - f + 2), i - f + 2);
            }
        }
    }

    //判断字符串是否为ip地址的合法数字
    bool lawful(string s, int begin, int end) {
        string flag = s.substr(begin, end - begin + 1);
        if (flag.size() > 1 && flag[0] == '0') return false;
        stringstream strs;
        int num;
        strs << flag;
        strs >> num;
        if (num > 255) return false;
        return true;
    }
};

[中等]括号生成

  • 原题链接
  • 题解
    leftk表示(还可以添加的个数,rightk表示)还可以添加的个数,并且rightk要大于等于leftk
class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        string str;
        recall(str,n,n);
        return ans;
    }

    /*
    leftk表示(还可以添加的个数
    rightk表示)还可以添加的个数
    并且rightk要大于等于leftk
    */
    void recall(string str, int leftk ,int rightk){
        if(leftk ==0 && rightk==0){
            ans.push_back(str);
        }
        if(leftk>0){
            str += '(';
            recall(str,leftk-1,rightk);
            str.erase(str.size() - 1, 1);//恢复现场
        }
        if(rightk>leftk && rightk>0){
            str += ')';
            recall(str,leftk,rightk-1);
            str.erase(str.size() - 1, 1);//恢复现场
        }
    } 
};

[中等]子集

  • 原题链接
  • 题解
    比较简单的回溯法,剪枝的主要思路就是保证递归循环之中选的数在k之后。
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> num;
    vector<vector<int>> subsets(vector<int>& nums) {
        ans.push_back(num);
        sort(nums.begin(),nums.end());
        recall(nums,0);
        return ans;
    }
    void recall(vector<int>& nums,int k){
        for(int i=0;i<nums.size();i++){
            if(i>=k){
                num.push_back(nums[i]);
                ans.push_back(num);
                recall(nums,i+1);
                num.pop_back();
            }
        }
    }
};

[中等]单词搜索

  • 原题链接
  • 题解
    ①调用recall()回溯函数的时候,每一步return最好是设置当前标记为0flag[i][j]=0,恢复现场
    ②如果你在return true的时候觉得可以不返回现场,又把flag数组设置为全局变量,可能会出现力扣和vs运行情况不同的问题,因为力扣同时会测多组数据,恢复现场没有做好,容易使全局中的flag标记没有回到清零的状态,会使第一个样例之后的部分样例结果错误。
class Solution {
public:
    int flag[7][7];
    bool exist(vector<vector<char>>& board, string word) {    
        //找出所有元素为word[0]的点,标记为flag = 1
        for (int i = 0; i < board.size(); i++)
            for (int j = 0; j < board[0].size(); j++)
                if (board[i][j] == word[0]) {
                    if (recall(board, word, 0, i, j)) return true;
                }
        return false;
    }
    bool recall(vector<vector<char>>& board, string word, int k, int x, int y) {
        //表示走过
        flag[x][y] = 1;
        if (board[x][y] != word[k]) {
            flag[x][y] = 0;
            return false;
        }
        //成功的返回条件
        if (k == word.size()-1){
            flag[x][y] = 0;
            return true;
        }
        //往上走
        if (x > 0 && !flag[x - 1][y]) {
            if (recall(board, word, k + 1, x - 1, y)) {
                flag[x][y] = 0;
                return true;
            }
        }
        //往下走
        if (x < board.size() - 1 && !flag[x + 1][y]) {
            if (recall(board, word, k + 1, x + 1, y)) {
                flag[x][y] = 0;
                return true;
            }
        }
        //往左走
        if (y > 0 && !flag[x][y - 1]) {
            if (recall(board, word, k + 1, x, y - 1)) {
                flag[x][y] = 0;
                return true;
            }
        }
        //往下走
        if (y < board[x].size() - 1 && !flag[x][y + 1]) {
            if (recall(board, word, k + 1, x, y + 1)) {
                flag[x][y] = 0;
                return true;
            }
        }
        flag[x][y] = 0;
        return false;
    }
};

[中等]组合

  • 原题链接
  • 题解
    比较简单的回溯+剪枝问题
    请添加图片描述
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> num;
    vector<vector<int>> combine(int n, int k) {
        recall(n,k,1);
        return ans;
    }
    /*
    p表示当前递归的层数,剪枝的思路是只选择大于p的数
    */
    void recall(int n,int k, int p){
        if(num.size() == k){
            ans.push_back(num);
            return;
        }
        for(int i=p;i<=n;i++){
            num.push_back(i);
            recall(n,k,i+1);
            num.pop_back();//恢复现场
        }
    }
};

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

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

相关文章

三天吃透消息队列面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

OSPF路由协议总结

OSPF路由协议总结一 OSPF协议的三个工作步骤二 5中OSPF协议报文三 4种网络类型&#xff0c;邻居和邻接3.1 P2P3.2 P2MP3.3 Broadcase3.4 NBMA&#xff08;非广播多路访问&#xff09;四 OSPF协议的度量方式五 LS&#xff08;链路状态&#xff09; LSA&#xff08;链路状态通告&…

JMU软件20 大数据技术复习(只写了对比18提纲的变动部分)

原博主 博客主页&#xff1a;https://xiaojujiang.blog.csdn.net/ 原博客链接&#xff1a;https://blog.csdn.net/qq_43058685/article/details/117883940 本复习提纲只适用于JMU软件工程大数据课程&#xff08;ckm授课&#xff09; 具体内容参考老师提纲的考纲&#xff0c;18和…

使用Tensorflow完成一个简单的手写数字识别

Tensorflow中文手册 介绍TensorFlow_w3cschool 模型结构图&#xff1a; 首先明确模型的输入及输出&#xff08;先不考虑batch&#xff09; 输入&#xff1a;一张手写数字图&#xff08;28x28x1像素矩阵&#xff09; 1是通道数 输出&#xff1a;预测的数字&#xff08;1x10的one…

100种思维模型之信息传递思维模型-028

人与人之间存有 认知偏差和理解偏差 &#xff0c;信息在传递过程中会 衰减、失真以及再加工 &#xff01; 信息传递思维模型 &#xff0c;一个有助于 提高信息传递质量 的思维模型。下面从三个方面进行介绍&#xff0c; 何谓信息传递思维模型、信息传递思模型生活中的运…

JVM系统优化实践(9):G1垃圾回收器

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;在JDK8及其之前&#xff0c;一直用的都是ParNewCMS的组合&#xff1a;ParNew负责年轻代的垃圾回收&#xff0c;而由CMS负责老年代的垃圾回收&#xff0c;但会产生…

脑机接口科普0016——独立BCI与非独立BCI

本文禁止转载&#xff01;&#xff01;&#xff01;&#xff01; 所谓的“独立BCI”与“非独立BCI”仅仅是BCI系统中的一个术语。本章主要是介绍一下这两个术语。 这两个术语是由Wolpaw在2002年提出来的。 独立BCI是指不依赖于中枢神经系统的的输出。 非独立BCI是指那种依赖…

工作5年了,你竟然还不会应用优雅停机?

事情是这样的&#xff0c;小明是一个工作五年的老程序员&#xff0c;半秃着的头已经彰显了他深不可测的技术实力。 这一天&#xff0c;小明收到了领导给过来的一个需求。 领导对小明说&#xff1a;“小明啊&#xff0c;你工作五年了&#xff0c;这个需求我交给你一个人负责很是…

开源写作平台WriteFreely(基础篇)

什么是 WriteFreely ? WriteFreely 是一个专为作家打造的干净、极简主义的出版平台。可以用来创建一个博客&#xff0c;在您的组织内分享知识&#xff0c;或者围绕共同的写作行为建立一个社区。 这里值得一提的是&#xff0c; WriteFreely 支持 ActivityPub 协议&#xff0c;这…

逐句回答,流式返回,ChatGPT采用的Server-sent events后端实时推送协议Python3.10实现,基于Tornado6.1

善于观察的朋友一定会敏锐地发现ChatGPT网页端是逐句给出问题答案的&#xff0c;同样&#xff0c;ChatGPT后台Api接口请求中&#xff0c;如果将Stream参数设置为True后&#xff0c;Api接口也可以实现和ChatGPT网页端一样的流式返回&#xff0c;进而更快地给到前端用户反馈&…

剑指offer在排序数组中的二分法应用总结

排序数组中的搜索问题&#xff0c;首先想到 二分法 解决&#xff0c;本篇详细解析关于二分法边界的问题。 目录 一、二分法概念 二、剑指Offer53.在排序数组中查找数字 三、在排序数组中查找元素的第一个和最后一个位置 一、二分法概念 二分法就是在一个有序递增的数组中进行…

使用RabbitMQ发送短信

1、在项目中分别创建模块financial-core、financial-mq、financial-sms&#xff0c;如图&#xff1a; 模块构成 <modules><module>financial-common</module><module>financial-base</module><module>financial-core</module><mo…

剑指 Offer 66. 构建乘积数组

摘要 剑指 Offer 66. 构建乘积数组 一、左右乘积列表 我们不必将所有数字的乘积除以给定索引处的数字得到相应的答案&#xff0c;而是利用索引左侧所有数字的乘积和右侧所有数字的乘积&#xff08;即前缀与后缀&#xff09;相乘得到答案。对于给定索引i&#xff0c;我们将使…

Qt广告机服务器(上位机)

目录功能结构adSever.promain.cpptcp_MSG.h 共用Tcp传输信息adsever.h 服务器adsever.cpp 服务器addate.h 时间处理addate.cpp 时间处理adtcp.h 客户端Socket处理adtcp.cpp 客户端Socket处理client.h 客户端信息类client.cpp 客户端信息类admsglist.h 信息记录模块admsglist.cp…

jupyter的使用

1.安装 安装过程看这篇记录。 安装 2.如何启动 环境搭建好后&#xff0c;本机输⼊jupyter notebook命令&#xff0c;会⾃动弹出浏览器窗⼝打开 Jupyter Notebook # 进⼊虚拟环境 workon ai(这个是虚拟环境的名称) # 输⼊命令 jupyter notebook本地notebook的默认URL为&…

宝藏级BI数据可视化功能|图表联动分析

在浏览其他人的BI数据可视化报表时&#xff0c;经常会发现这样的一个现象&#xff0c;点一下上一张数据可视化图表中的某个门店&#xff0c;下一张图表将立即针对该门店展开数据可视化分析。这是什么效果&#xff1f;怎么实现&#xff1f;BI软件中还有多少宝藏级BI数据可视化功…

Oracle表分区的创建、新增、拆分

Oracle中为了方便管理、查询数据当数据量大于500w或者2G时最好用分区表&#xff0c;常见的一种是使用时间作为分区。 分区表添加新的分区有 2 种情况&#xff1a; (1) 原分区里边界是 maxvalue 或者 default。 这种情况下&#xff0c;我们需要把边界分区 drop 掉&#xff0c;加…

好的计划是成功的一半,如何制定项目计划?

好的计划是成功的一半&#xff0c;不好的计划会使项目一步步失败&#xff0c;任何事情&#xff0c;要取得成功&#xff0c;离不开一个科学合理的计划。 计划是为了实现项目所提出的各项目标&#xff0c;每一项任务都是针对某一个特定目标的&#xff0c;因此&#xff0c;一项计划…

计算机视觉手指甲标注案例

关键点标注是指识别和标注图像或视频中特定的相关点或区域的过程。在机器学习行业&#xff0c;它经常被用来训练计算机视觉模型&#xff0c;以执行诸如物体检测、分割和跟踪等任务。 关键点注释可用于以下应用&#xff1a; 面部关键点检测&#xff1a;识别图像中人脸上的眼睛…

12.SpringSecurity中OAuth2.0的实现

一、OAuth2.0介绍 1.概念说明 https://oauth.net/2/ 先说OAuth&#xff0c;OAuth是Open Authorization的简写。   OAuth协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息&#xff08;如…