当前位置: 首页 > news >正文

leetcode算法刷题的第二十一天

1.leetcode 93.复原IP地址

题目链接

class Solution {
public:vector<string> result;// 记录结果// 判断字符串s在左闭右闭区间[start, end]所组成的数字是否合法bool isValid(const string& s,int start,int end){if(start>end){return false;}if(s[start]=='0'&&start!=end){// 0开头的数字不合法return false;}int num=0;for(int i=start;i<=end;i++){if(s[i]>'9'||s[i]<'0'){// 遇到非数字字符不合法return false;}num=num*10+(s[i]-'0');if(num>255){return false;// 如果大于255了不合法}}return true;}// startIndex: 搜索的起始位置,pointNum:添加逗点的数量,相当于树形结构的深度void backtracking(string& s,int startIndex,int pointNum){if(pointNum==3){// 逗点数量为3时,分隔结束// 判断第四段子字符串是否合法,如果合法就放进result中if(isValid(s,startIndex,s.size()-1)){result.push_back(s);}return;}for(int i=startIndex;i<s.size();i++){if(isValid(s,startIndex,i)){// 判断 [startIndex,i] 这个区间的子串是否合法s.insert(s.begin()+i+1,'.');// 在i的后面插入一个逗点pointNum++;backtracking(s,i+2,pointNum);// 插入逗点之后下一个子串的起始位置为i+2pointNum--;// 回溯s.erase(s.begin()+i+1);// 回溯删掉逗点}else break;// 不合法,直接结束本层循环}}vector<string> restoreIpAddresses(string s) {if(s.size()<4||s.size()>12) return result;backtracking(s,0,0);return result;}
};

思路总结:

这道题目相信大家刚看的时候,应该会一脸茫然。

其实只要意识到这是切割问题,切割问题就可以使用回溯搜索法把所有可能性搜出来。

切割问题可以抽象为树型结构。

最后就是在写一个判断段位是否是有效段位了。

主要考虑到如下三点:

段位以0为开头的数字不合法

段位里有非正整数字符不合法

段位如果大于255了不合法

在分割回文串中我列举的分割字符串的难点,这道题基本都覆盖了,而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性,可以说是分割回文串的加强版了,如果大家能把树形结构图画出来的话,应该就能很好的理解题意。

2.leetcode 78.子集

题目链接

class Solution {
public:vector<vector<int>> result;//收集结果集vector<int> path;//收集回溯的路径void backtracking(vector<int>& nums,int startIndex){result.push_back(path);//收集子集,要放在终止添加的上面,否则的话会漏掉自己//空集也可以是自己的子集if(startIndex>=nums.size()){//终止条件可以不加return;}for(int i=startIndex;i<nums.size();i++){path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsets(vector<int>& nums) {backtracking(nums,0);return result;}
};

思路总结:

如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。

那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

有同学问了,什么时候for可以从0开始呢?

求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合,排列问题我们后续的博客就会讲到的。

在注释中,我们可以看到可以不写终止条件,因为我们本来就要遍历整棵树。有的人可能会担心不写终止条件会不会无限递归?并不会,因为每次递归的下一层就是从i+1开始的。

3.leetcode 90.子集II

题目链接

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums,int startIndex,vector<bool>& used){result.push_back(path);if(startIndex>=nums.size()){return;}for(int i=startIndex;i<nums.size();i++){// used[i - 1] == true,说明同一树枝candidates[i - 1]使用过// used[i - 1] == false,说明同一树层candidates[i - 1]使用过// 而我们要对同一树层使用过的元素进行跳过if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false){continue;}path.push_back(nums[i]);used[i]=true;backtracking(nums,i+1,used);used[i]=false;path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {vector<bool> used(nums.size(),false);sort(nums.begin(),nums.end());//对于这种去重,我们先要对其进行排序backtracking(nums,0,used);return result;}
};

补充:本题也可以不使用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。

class Solution {
public:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& nums,int startIndex){result.push_back(path);for(int i=startIndex;i<nums.size();i++){//而我们要对同一树层使用过的元素进行跳过if(i>startIndex&&nums[i]==nums[i-1]){//注意这里是i>startIndexcontinue;}path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {sort(nums.begin(),nums.end());//去重需要排序backtracking(nums,0);return result;}
};

思路总结:这道题目和上一题的区别在于集合里面就已经有重复的元素了,而且求取的子集要去重。这里我们要理解什么是树层去重和树枝去重,这个很重要。注意:去重需要先对集合排序。

同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集!如果之前的子集问题和去重问题都可以掌握的好,这道题目可以分分钟AC。

http://www.dtcms.com/a/357723.html

相关文章:

  • 阿里云服务器购买流程:四种主要购买方式图文教程详解与选择参考
  • Cherrystudio的搭建和使用
  • Silvaco TCAD | Victory DoE的基本使用方法(三)
  • 小杰机器视觉(six)——模板匹配
  • LeetCode 01背包 494. 目标和
  • 顶点 (VS)vs 片段(FS):OpenGL纹理滚动着色器的性能博弈与设计哲学
  • Java进阶教程之多线程与并发编程
  • Windows下快速配置UDF编译环境的详细步骤
  • VexCL并行异构库介绍和使用
  • Python Imaging Library (PIL) 全面指南:PIL图像处理异常处理与优化
  • oceanbase-参数及变量的记录
  • LeetCode 刷题【56. 合并区间】
  • 新人桌球笔记
  • Apisix工作流程
  • 主流国产数据库:文档完备性
  • 进程与线程的根本区别
  • 【双指针 - LeetCode】42. 接雨水
  • gstreamer使用hook的简单示例
  • 用户自定义字段(Custom Fields)设计方案,兼顾多语言、分组、校验、权限、查询性能、审计与多租户
  • LeetCode - 128. 最长连续序列
  • LeetCode第二题知识点3 ----引用类型
  • lxml库如何使用
  • DSP280049 CLA可访问资源
  • 【开题答辩全过程】以 非遗信息管理系统为例,包含答辩的问题和答案
  • 2025年企业管理与经济、文化发展国际会议(MECD 2025)
  • 拎包入住搭建 Browser Use Agent:基于PPIO Model API +Agent 沙箱的一体化构建
  • React-Native项目回忆
  • QML Chart组件之坐标轴共有属性
  • 基于Springboot + vue3实现的教育资源共享平台
  • Java流程控制03——顺序结构(本文为个人学习笔记,内容整理自哔哩哔哩UP主【遇见狂神说】的公开课程。 > 所有知识点归属原作者,仅作非商业用途分享)