day24 | lc93复制ip地址 | lc78子集 | lc90子集II
day24
lc93_复原IP地址
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
本题不仅要对输入的数进行切割 还要对切割的数进行合法性检查
什么是不合法的ip呢?
转换成n叉树

难点:
- 切割的子串怎么表达范围呢
使用startIndex 最后一个子串的范围极速 [start, s.length() - 1]
- 合法性怎么判断
- 数字前面不能有0 (可以只有一个0 ,但是0不能作为开头)
- 每个范围小于255
- 得是正整数字符
- 切割线怎么模拟
在代码中 startIndex就是分割线
回溯三部曲:
- 回溯函数的参数, 返回值
- void backtracking()
- 参数 : 结果集res, 小数点个数pointSum 起始位置startIndex
- 终止条件
- 小数点数量等于3个 并且最后一个子串合法 加入结果集 然后return
- 单层递归回溯逻辑
- 合法性判断: for循环之后 对切割的子串就要合法性判断 合法才进行下去 子串范围是[sIndex , i ]
- 改造字符串s : 插入小数点 插入位置为 开始位置+i + 1 然后小数点数量加1
- 开始递归: 起始位置变成i + 2 因为还要算上插入的小数点
- 回溯 : 删除插入的小数点 小数点数量减1;
class Solution {
public List<String> restoreIpAddresses(String s) {
List<String> res = new ArrayList<>();
StringBuilder sb = new StringBuilder(s); // 初始化 StringBuilder
backtracking(sb, res, 0, 0);
return res;
}
public void backtracking(StringBuilder s, List<String> res, int pointSum, int sIndex) {
// 如果已经插入3个点,检查最后一段是否合法
if (pointSum == 3 && check(s, sIndex, s.length() - 1)) {
res.add(s.toString()); // 添加合法结果
return;
}
for (int i = sIndex; i < s.length(); i++) {
if (check(s, sIndex, i)) { // 检查当前段是否合法
s.insert(i + 1, '.'); // 插入.点
backtracking(s, res, pointSum + 1, i + 2); // 递归调用
s.deleteCharAt(i + 1); // 回溯,删除.点
} else {
break; // 如果当前段不合法,直接退出
}
}
}
public boolean check(StringBuilder s, int l, int r) {
if (l > r) return false; // 起始位置大于结束位置
if (s.charAt(l) == '0' && l != r) return false; // 前导0不合法
int size = 0;
for (int i = l; i <= r; i++) {
int digit = s.charAt(i) - '0'; // 将字符转为数字
size = size * 10 + digit;
if (size > 255) return false; // 超过255不合法
}
return true;
}
}
lc78_子集(不重复)
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
子集是收集树形结构中树的所有节点的结果。
而组合问题、分割问题是收集树形结构中叶子节点的结果。
套模板得出代码
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
backtracking(nums, 0, res, path);
return res;
}
public void backtracking(int[] arr, int sIndex, List<List<Integer>> res, List<Integer> path){
res.add(new ArrayList<>(path));
if (sIndex >= nums.length) return;
for(int i = sIndex; i < arr.length; i++){
path.add(arr[i]);
backtracking(arr, i + 1, res, path);
path.remove(path.size() - 1);
}
}
}
lc90_子集II(重复)
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
- 输入: [1,2,2]
- 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
这道题目和lc78的区别就是集合里有重复元素了,而且求取的子集要去重
去重 可以只使用startIndex进行去重, 因为递归的时候下一个startIndex是i+1而不是0。
先进行排序 让相等的两个在一起 然后进行continue
但如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
Arrays.sort(nums);
backtracking(nums, 0, path, res);
return res;
}
public void backtracking(int[] arr, int sIndex, List<Integer> path, List<List<Integer>> res){
res.add(new ArrayList<>(path));
if(sIndex >= arr.length) return;
for(int i = sIndex; i < arr.length; i++){
if(i > sIndex && arr[i - 1] == arr[i]) continue;
path.add(arr[i]);
backtracking(arr, i + 1, path, res);
path.remove(path.size() - 1);
}
}
}

浙公网安备 33010602011771号