[豪の算法奇妙冒险] 代码随想录算法训练营第二十四天 | 93-复原IP地址、78-子集、90-子集Ⅱ
代码随想录算法训练营第二十四天 | 93-复原IP地址、78-子集、90-子集Ⅱ
LeetCode93 复原IP地址
题目链接:https://leetcode.cn/problems/restore-ip-addresses/description/
文章讲解:https://programmercarl.com/0093.复原IP地址.html
视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/?vd_source=b989f2b109eb3b17e8178154a7de7a51
切割问题就可以使用回溯搜索法把所有可能性搜出来,抽象为树型结构
本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。dotCnt表示逗点数量,dotCnt为3说明字符串已被分成4段了,然后验证一下第四段是否合法,如果合法就将该字符串加入到结果集里
单层逻辑中,如果合法就在字符串后面加上符号 . 表示已经分割;如果不合法就结束本层循环,剪掉同层剩余分支
判断分割的字符子串是否合法,主要考虑:以0为开头的数字不合法,有非正整数字符不合法、数字大于255了不合法、索引指针start大于end不合法

class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
StringBuilder str = new StringBuilder(s);
backTracking(str, 0, 0);
return result;
}
public boolean isValid(StringBuilder str, int start, int end){
if(start > end){
return false;
}
if(str.charAt(start) == '0' && start != end){
return false;
}
int num = 0;
for(int i = start; i <= end; i++){
int digit = str.charAt(i) - '0';
num = 10*num + digit;
if(num > 255){
return false;
}
}
return true;
}
public void backTracking(StringBuilder str, int start, int dotCnt){
if(dotCnt == 3 && isValid(str, start, str.length()-1)){
result.add(str.toString());
return;
}
for(int i = start; i < str.length(); i++){
if(isValid(str, start, i)){
str.insert(i+1, '.');
backTracking(str, i+2, dotCnt+1);
str.deleteCharAt(i+1);
}else{
break;
}
}
}
}
LeetCode78 子集
题目链接:https://leetcode.cn/problems/subsets/description/
文章讲解:https://programmercarl.com/0078.子集.html
视频讲解:https://www.bilibili.com/video/BV1U84y1q7Ci/?vd_source=b989f2b109eb3b17e8178154a7de7a51
如果把子集问题、组合问题、分割问题都抽象为树形结构的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点
子集也是一种组合问题,因为它是无序的,取过的元素不会重复取,写回溯算法的时候,for要从startIndex开始,而不是从0开始

class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> records = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backTracking(nums, 0);
return result;
}
public void backTracking(int[] nums, int start){
result.add(new ArrayList<>(records));
for(int i = start; i < nums.length; i++){
records.add(nums[i]);
backTracking(nums, i+1);
records.remove(records.size()-1);
}
}
}
LeetCode90 子集Ⅱ
题目链接:
文章讲解:https://programmercarl.com/0090.子集II.html
视频讲解:https://www.bilibili.com/video/BV1vm4y1F71J/?vd_source=b989f2b109eb3b17e8178154a7de7a51
元素在同一个组合内是可以重复的,但两个组合不能相同,所以要树层去重,树枝不用去重
树层去重的思路和 LeetCode40 组合总和Ⅱ 的很相似,如果 nums[i] == nums[i - 1] 并且 used[i - 1] == false,就说明前一个树枝使用了nums[i - 1],也就是说同一树层使用过nums[i - 1],此时for循环里就应该做continue的操作,做树层的去重

class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> records = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
boolean[] used = new boolean[nums.length];
backTracking(nums, 0, used);
return result;
}
public void backTracking(int[] nums, int start, boolean[] used){
result.add(new ArrayList<>(records));
for(int i = start; i < nums.length; i++){
if(i > 0 && nums[i] == nums[i-1] && used[i-1] == false){
continue;
}
records.add(nums[i]);
used[i] = true;
backTracking(nums, i+1, used);
used[i] = false;
records.remove(records.size()-1);
}
}
}

浙公网安备 33010602011771号