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

代码随想录打卡|Day21(复原ip地址、子集、子集2)

回溯算法 Part03

补充小知识(字符串的常用方法)

操作类型方法/类说明示例(Java)
截取substring()提取字符串的指定部分"Hello".substring(1, 3)"el"
连接concat() / +拼接字符串"A".concat("B")"A" + "B""AB"
可变字符串StringBuilder高效修改字符串(非线程安全)new StringBuilder("Hi").append("!")"Hi!"
查找indexOf()返回字符/子串的首次出现位置"apple".indexOf('p')1
替换replace()替换字符或子串"abc".replace("b", "x")"axc"
分割split()按正则表达式分割字符串"a,b,c".split(",")["a", "b", "c"]
大小写转换toLowerCase()/toUpperCase()转换大小写"Hi".toUpperCase()"HI"
去除空格trim()移除首尾空白字符" a ".trim()"a"
格式化String.format()格式化字符串(类似printfString.format("%s:%d", "ID", 10)"ID:10"
比较equals()/compareTo()比较内容或字典序"a".equals("A")false; "a".compareTo("b")-1
正则匹配matches()检查字符串是否匹配正则表达式"123".matches("\\d+")true
字符提取charAt()获取指定位置的字符"cat".charAt(1)'a'
长度length()返回字符串长度"hello".length()5
类型转换valueOf()将其他类型转为字符串String.valueOf(1.23)"1.23"
反转StringBuilder.reverse()反转字符串(需配合StringBuildernew StringBuilder("123").reverse()"321"
空白检查isBlank() (Java 11+)检查字符串是否为空或仅含空白字符" ".isBlank()true

复原ip地址

力扣题目链接
代码随想录链接
视频链接

题目描述: 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。

例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
在这里插入图片描述
思路:本题的结束如下所示(代码随想录):
在这里插入图片描述

回溯法

法一(使用substring方法对字符串进行操作)

//方法二:比上面的方法时间复杂度低,更好地剪枝,优化时间复杂度
class Solution {// 定义全局变量结果List<String> result = new ArrayList<>();public List<String> restoreIpAddresses(String s) {backTracking(s,0,0);return result;}// 定义回溯函数private void backTracking(String s , int startIndex , int pointNum){// 定义结果的返回条件if(pointNum == 3){if(isValid(s,startIndex,s.length() - 1)){result.add(s);}return ;}for(int i = startIndex ; i < s.length() ; i ++){if(pointNum > 3)return ;if(isValid(s,startIndex,i)){s = s.substring(0,i+1)+"."+s.substring(i+1);pointNum += 1;// 此处为i+2的原因是,加了一个"."backTracking(s,i+2,pointNum);// 回溯pointNum -= 1;s = s.substring(0,i + 1) + s.substring(i+2);}else{break;}}}private boolean isValid(String s , int start , int end){if(start > end) return false;if(s.charAt(start) == '0' && start != end) return false;int num = 0 ;for(int i = start ; i <= end ; i++){if(s.charAt(i) > '9' || s.charAt(i) < '0')return false;num = num*10 +(s.charAt(i) - '0');if(num > 255)return false; }return true;}
}

回溯法(使用StringBuilder)

StringBuilder和subtring的优劣

  • StringBuilder 向字符串之中插入字符的时候无需复制整个字符串,从而减少了操作的时间复杂度,也不用开辟内存空间用于存储substring,减少了空间复杂度
  • StringBuider非线程安全

StringBuilder常用方法介绍:

方法名作用描述参数说明返回值类型示例(输入 → 输出)
​**append(x)**追加任意类型数据(自动转为字符串)x: 基本类型/对象/StringStringBuildersb.append("Hi").append(1)"Hi1"
​**insert(idx, x)**在指定位置插入数据idx: 插入索引;x: 要插入的内容StringBuildersb.insert(1, "X")"HXi1"
​**delete(start, end)**删除子串(含头不含尾)start: 起始索引;end: 结束索引StringBuildersb.delete(1, 2)"Hi1"
​**reverse()**反转字符串内容StringBuildersb.reverse()"1iH"
​**replace(start, end, str)**替换指定区间的字符为另一字符串start, end: 替换范围;str: 新字符串StringBuildersb.replace(0, 1, "A")"AiH"
​**charAt(idx)**获取指定位置的字符idx: 字符索引charsb.charAt(0)'A'
​**setCharAt(idx, c)**修改指定位置的字符idx: 索引;c: 新字符voidsb.setCharAt(0, 'B')"BiH"
​**length()**返回当前字符序列长度intsb.length()3
​**capacity()**返回当前底层数组容量(≥length)intnew StringBuilder(10).capacity()10
​**ensureCapacity(min)**确保容量至少为指定值(自动扩容时通常为 2*旧容量+2min: 最小容量voidsb.ensureCapacity(20)
​**toString()**转为不可变String对象Stringsb.toString()"BiH"
​**substring(start[, end])**截取子串(返回String,不影响原内容)start/end: 截取范围Stringsb.substring(1)"iH"
​**indexOf(str[, fromIdx])**查找子串首次出现的位置str: 目标字符串;fromIdx: 起始索引intsb.indexOf("i")1
​**lastIndexOf(str)**查找子串最后一次出现的位置indexOf()intsb.append("i").lastIndexOf("i")2
​**deleteCharAt(idx)**删除指定位置的字符idx: 字符索引StringBuildersb.deleteCharAt(0)"iH"
​**setLength(len)**强制设置字符序列长度(截断或填充空字符\u0000len: 新长度voidsb.setLength(2)"Bi"
​**trimToSize()**释放多余容量(将底层数组缩至与length相同)voidsb.trimToSize()

代码如下:

//方法二:比上面的方法时间复杂度低,更好地剪枝,优化时间复杂度
class Solution {// 定义全局变量结果List<String> result = new ArrayList<>();public List<String> restoreIpAddresses(String s) {StringBuilder sb = new StringBuilder(s);backTracking(sb,0,0);return result;}// 定义回溯函数private void backTracking(StringBuilder s , int startIndex , int numOfDot){if(numOfDot == 3){if(isValid(s,startIndex,s.length() - 1)){result.add(s.toString());}return ;}for(int i = startIndex ; i < s.length() ; i++){if(isValid(s,startIndex , i)){s.insert(i+1,".");numOfDot += 1;backTracking(s,i+2,numOfDot);numOfDot -= 1;s.deleteCharAt(i+1);}}}private boolean isValid(StringBuilder s , int start , int end){if(start > end) return false;if(s.charAt(start) == '0' && start != end) return false;int num = 0 ;for(int i = start ; i <= end ; i++){if(s.charAt(i) > '9' || s.charAt(i) < '0') return false;num = num*10 + (s.charAt(i) - '0');if(num > 255) return false;}return true;}
}

在代码之中,还可以进一步剪枝

例如将判断字符串是否合法的部分直接写道回溯递归部分,从而直接免去不必要的操作

class Solution {List<String> result = new ArrayList<String>();StringBuilder stringBuilder = new StringBuilder();public List<String> restoreIpAddresses(String s) {restoreIpAddressesHandler(s, 0, 0);return result;}// number表示stringbuilder中ip段的数量public void restoreIpAddressesHandler(String s, int start, int number) {// 如果start等于s的长度并且ip段的数量是4,则加入结果集,并返回if (start == s.length() && number == 4) {result.add(stringBuilder.toString());return;}// 如果start等于s的长度但是ip段的数量不为4,或者ip段的数量为4但是start小于s的长度,则直接返回if (start == s.length() || number == 4) {return;}// 剪枝:ip段的长度最大是3,并且ip段处于[0,255]for (int i = start; i < s.length() && i - start < 3 && Integer.parseInt(s.substring(start, i + 1)) >= 0&& Integer.parseInt(s.substring(start, i + 1)) <= 255; i++) {if (i + 1 - start > 1 && s.charAt(start) - '0' == 0) {break;}stringBuilder.append(s.substring(start, i + 1));// 当stringBuilder里的网段数量小于3时,才会加点;如果等于3,说明已经有3段了,最后一段不需要再加点if (number < 3) {stringBuilder.append(".");}number++;restoreIpAddressesHandler(s, i + 1, number);number--;// 删除当前stringBuilder最后一个网段,注意考虑点的数量的问题stringBuilder.delete(start + number, i + number + 2);}}
}

子集

力扣题目链接
代码随想录链接
视频链接

题目描述: 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

在这里插入图片描述

思路:这道题不算难题,只要将遍历过的所有情况都保存下来即可,本题的解树为:
在这里插入图片描述

本题的代码为:

class Solution {List<List<Integer>> result = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> subsets(int[] nums) {backTracking(nums,0);return result;}private void backTracking(int[] nums , int startIndex){// 将所有的结果均记录下来result.add(new ArrayList(path));if(startIndex > nums.length - 1 ){return ;}for(int i = startIndex ; i < nums.length ; i++){path.add(nums[i]);backTracking(nums,i + 1);path.remove(path.size() - 1);}}
}

子集2

力扣题目链接
代码随想录链接
视频链接

题目描述: 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

在这里插入图片描述

思路:我们对于组合问题进行去重的时候,一般会初始化used数组用于标识数据是否被使用过。但是我们一定要分清楚什么时候使用树层去重,什么时候是树枝去重。
在这里插入图片描述

树层去重讲解链接

回溯法

代码如下:

class Solution {List<List<Integer>> result = new ArrayList<>();List<Integer> path = new ArrayList<>();boolean[] used;public List<List<Integer>> subsetsWithDup(int[] nums) {Arrays.sort(nums);used = new boolean[nums.length];Arrays.fill(used,false);backTracking(nums,0);return result;}// 定义回溯函数private void backTracking(int[] nums , int startIndex){result.add(new ArrayList(path));if(startIndex > nums.length -1)return ;for(int i = startIndex ; i <= nums.length -1 ; i++){// 树层去重if( i > 0 && nums[i] == nums[i - 1]&&!used[i - 1] )continue;used[i] = true;path.add(nums[i]);backTracking(nums,i+1);used[i] = false;path.removeLast();}}
}

相关文章:

  • Unity游戏开发实战:从PlayerPrefs到JSON,精通游戏存档与加载机制
  • Python 跨平台系统资源监控实践
  • RS232实现主单从多通讯
  • 健身会员管理系统(ssh+jsp+mysql8.x)含运行文档
  • Python实现的智能商品推荐系统分享+核心代码
  • 基于SFC的windows修复程序,修复绝大部分系统损坏
  • 通过Xshell上传文件到Linux
  • OrbisGIS:基于Java开发的开源GIS软件
  • 大型旋转机械声信号分析处理与故障诊断模块SoundAgent
  • 软件架构分层策略对比及Go项目实践
  • 历史文化探险,梧州旅游景点推荐
  • DNS主从同步
  • 【人工智能】控制专业的职业发展方向
  • 指针----------C语言经典题目(2)
  • STM32单片机入门学习——第43节: [12-3] 读写备份寄存器实时时钟
  • 无需训练的具身导航探索!TRAVEL:零样本视觉语言导航中的检索与对齐
  • 山东科技大学人工智能原理考试回忆复习资料
  • python基础知识点(1)
  • 猫咪如厕检测与分类识别系统系列【十二】猫咪进出事件逻辑及日志优化
  • 【Datawhale AI春训营】Java选手初探数据竞赛
  • 官方披露:定西民政局原局长将收受烟酒高价“倒卖”给单位,用于违规接待
  • 北京理工大学解除宫某聘用关系,该教授此前被指骚扰猥亵学生
  • 海口市美兰区委副书记、区长吴升娇去世,终年41岁
  • 亚太峰会上哪个词最火?我们问了问AI
  • 财政部关于六起地方政府隐性债务问责典型案例的通报
  • 卡洛·金茨堡:女巫与萨满——我的学术之路