第七章 回溯算法part03
93.复原IP地址
本期本来是很有难度的,不过 大家做完 分割回文串 之后,本题就容易很多了
题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html
视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/
78.子集
子集问题,就是收集树形结构中,每一个节点的结果。 整体代码其实和 回溯模板都是差不多的。
题目链接/文章讲解:https://programmercarl.com/0078.%E5%AD%90%E9%9B%86.html
视频讲解:https://www.bilibili.com/video/BV1U84y1q7Ci
90.子集II
大家之前做了 40.组合总和II 和 78.子集 ,本题就是这两道题目的结合,建议自己独立做一做,本题涉及的知识,之前都讲过,没有新内容。
题目链接/文章讲解:https://programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html
视频讲解:https://www.bilibili.com/video/BV1vm4y1F71J
93.复原IP地址
题目链接
https://leetcode.cn/problems/restore-ip-addresses/description/
解题思路
切割问题就可以使用回溯搜索法把所有可能性搜出来,和131.分割回文串 十分类似了
利用startIndex找分割点
code
class Solution {
List<String> res=new ArrayList<>();
List<String> path=new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
backtracking(s,0);
return res;
}
public void backtracking(String s ,int startIndex){
if(path.size()==4){
StringBuilder sb=new StringBuilder();
sb.append(path.get(0)).append(".");
sb.append(path.get(1)).append(".");
sb.append(path.get(2)).append(".");
sb.append(path.get(3));
res.add(sb.toString());
return;
}
for(int i=startIndex;i<s.length();i++){
String str=s.substring(startIndex,i+1);
if(!isIp(str)){
continue;
}
//剪枝 如果size是1 剩余长度大于3个3位数数则剪枝 如长度是12 i=0的加入到size=1 此时i=1
//那么包含i=1的数就是 s.length()-1-i+1 就是剩余的长度且包含i位置 s.length()-i
if(path.size()==1 && s.length()-i>9){
break;
}
if(path.size()==2 && s.length()-i>6){
break;
}
if(path.size()==3 && s.length()-i>3){
break;
}
if(path.size()==3&&i!=s.length()-1){
continue;
}
path.add(str);
backtracking(s,i+1);
path.remove(path.size()-1);
}
}
public boolean isIp(String str){
if(str.length()>3){
return false;
}
if(str.length()>1&&'0'==str.charAt(0)){
return false;
}
int strToInt=Integer.parseInt(str);
if(strToInt<0 || strToInt >255){
return false;
}
return true;
}
}
78.子集
题目链接
解题思路
组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点
不重复,startIndex=i+1 取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始
code
class Solution {
List<List<Integer>> res =new ArrayList<>();
List<Integer> path=new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return res;
}
public void backtracking(int[] nums,int startIndex){
res.add(new ArrayList<>(path));
for(int i=startIndex;i<nums.length;i++){
path.add(nums[i]);
backtracking(nums,i+1);
path.remove(path.size()-1);
}
}
}
90.子集II
题目链接
解题思路
题目说可能包含重复元素,解集不能包含重复元素的问题
通用解法,和40.组合总和II 一个解法
要排序(把相邻的元素放在一起),相同元素跳过,(否则注定会和前面的元素取出重复的子集(组合)1 1 2 第一个1取的元素已经包含第二个1取的所有元素还包含了第二个1 所以要跳过 以达到去重 “数层去重”) startIndex=i+1
code
class Solution {
List<List<Integer>> res =new ArrayList<>();
List<Integer> path=new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backtracking(nums,0);
return res;
}
public void backtracking(int[] nums,int startIndex){
res.add(new ArrayList<>(path));
for(int i=startIndex;i<nums.length;i++){
if(i>startIndex && nums[i]==nums[i-1]){
continue;
}
path.add(nums[i]);
backtracking(nums,i+1);
path.remove(path.size()-1);
}
}
}
“树层去重”和“树枝去重” 注意去重需要先对集合排序
从图中可以看出,同一树层上重复取2 就要过滤掉,同一树枝上就可以重复取2,因为同一树枝上元素的集合才是唯一子集