当前位置: 首页 > 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.xdnf.cn/news/1384615.html

相关文章:

  • C# 一个投资跟踪程序的设计与实现:面向对象与设计模式的深度解析
  • Ansys 19 Mechanical 流体密封分析
  • Claude Code 完整手册:从入门、配置到高级自动化
  • 上海市赛/磐石行动2025决赛awd web2-python 4个漏洞详解
  • Java 将HTML文件、HTML字符串转换为图片
  • 抖音基于Flink的DataOps能力实践
  • 洞悉核心,驭数而行:深入理解 Oracle SQL 优化器(RBO 与 CBO)的性能调优哲学
  • SQL优化--OR
  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(四)
  • iOS混淆工具实战 电商类 App 的数据与交易安全防护
  • [awesome-nlp] docs | 精选NLP资源 | 分类
  • 三遥馈线终端:全球配电智能化浪潮下的核心设备与市场格局
  • 技术演进中的开发沉思-83 Linux系列: 信号
  • 把 AI 塞进「智能门锁」——基于指纹和语音双模态的零样本离线门禁系统
  • Spring Boot中MyBatis Provider注解实现动态SQL
  • 云手机中的多开功能具体是指什么?
  • DVWA靶场通关笔记-暴力破解(Impossible级别)
  • Android 14 PMS源码分析
  • 临床研究三千问——如何将临床问题转换成科学问题(7)
  • 【网络安全领域】边界安全是什么?目前的发展及应用场景
  • Nessus 是一款免费功能强大的漏洞扫描工具,广泛用于网络安全评估。
  • eslasticsearch+ik分词器+kibana
  • 【MySQL】练习12-2:配置复制
  • 国产数据库转型指南:DBA技能重构与职业发展
  • Unity RectTransform容易混淆的基础问题
  • 3471. 找出最大的几近缺失整数
  • MyBatis延迟加载
  • LaunchScreen是啥?AppDelegate是啥?SceneDelegate是啥?ContentView又是啥?Main.storyboard是啥?
  • DoIP路由激活报文
  • 玄机靶场 | 第九章-blueteam 的小心思3