93. 复原IP地址
本期本来是很有难度的,不过 大家做完 分割回文串 之后,本题就容易很多了题目链接/文章讲解:代码随想录
视频讲解:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址_哔哩哔哩_bilibili
重点:
1. startIndex作为切割线
2. for循环,循环的是树的其中一层也就是当前切割线。递归,是递归到下一层,也就是下一根切割线
思路:
递归+回溯:
1. 确定参数及返回值
private void backtracking(String s, int startIndex, int pointSum)
2. 确定终止条件
判断当前画了几个点了,到了3个就切割完成,检查最后一部分是否满足IP要求。前面的部分在for循环的时候就检查了
if (pointSum == 3) { if (isValid(s, startIndex, s.length() - 1)) { result.add(s); } return; }
3. 确定单层递归的逻辑
for循环移动的是当前的分割线,判断当前分割线有多少种可能。当前分割线出来的如果合法就可以固定这根分割线的情况下,递归去判断下一根分割线的情况了。backtracking(s, i + 2, pointSum),这里i+2是因为多加了点来分割了。
for (int i = startIndex; i < s.length(); i++) { // startIndex和i都是指切割线前一个 if (isValid(s, startIndex, i)) { StringBuilder stringBuilder = new StringBuilder(s); stringBuilder.insert(i + 1, '.'); s = stringBuilder.toString(); pointSum++; backtracking(s, i + 2, pointSum); pointSum--; stringBuilder.deleteCharAt(i + 1); s = stringBuilder.toString(); } else { break; } }
List<String> result;
public List<String> restoreIpAddresses(String s) {
result = new ArrayList<>();
backtracking(s, 0, 0);
return result;
}
private void backtracking(String s, int startIndex, int pointSum) {
// 达到三根分割线,判断最后一部分是否满足IP要求
if (pointSum == 3) {
if (isValid(s, startIndex, s.length() - 1)) {
result.add(s);
}
return;
}
// 当前层 - 不断移动当前这一根分割线
for (int i = startIndex; i < s.length(); i++) {
// startIndex和i都是指切割线前一个
if (isValid(s, startIndex, i)) {
StringBuilder stringBuilder = new StringBuilder(s);
stringBuilder.insert(i + 1, '.');
s = stringBuilder.toString();
pointSum++;
// 递归下一根分割线的情况
backtracking(s, i + 2, pointSum);
// 回溯回来
pointSum--;
stringBuilder.deleteCharAt(i + 1);
s = stringBuilder.toString();
} 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) < '0' || s.charAt(i) > '9') {
return false;
}
num = num * 10 + (s.charAt(i) - '0');
if (num > 255) {
return false;
}
}
return true;
}
78. 子集
子集问题,就是收集树形结构中,每一个节点的结果。 整体代码其实和 回溯模板都是差不多的。题目链接/文章讲解:代码随想录
视频讲解:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集_哔哩哔哩_bilibili
重点:
1. 回溯算法的模板
2. 每层递归都把path加入到result
思路:
递归+回溯:
1. 确定参数及返回值
private void backtracking(int[] nums, int startIndex)
2. 确定终止条件
叶子节点终止,其实不写也可以,因为for循环自动终止
if (startIndex >= nums.length) { return; }
3. 确定单层递归的逻辑
一个for代表当前的一层,递归嵌套一个for就是代表下一层,在每个递归开始的时候收集path到result
// for循环代表树的某一层 for (int i = startIndex; i < nums.length; i++) { path.add(nums[i]); // 递归嵌套下一层for backtracking(nums, i + 1); // 回溯整理path path.remove(path.size() - 1); }
List<Integer> path;
List<List<Integer>> result;
public List<List<Integer>> subsets(int[] nums) {
result = new ArrayList<>();
path = new ArrayList<>();
result.add(new ArrayList<>());
backtracking(nums, 0);
return result;
}
private void backtracking(int[] nums, int startIndex) {
// 把path加入到result
result.add(new ArrayList<>(path));
// 终止条件
if (startIndex >= nums.length) {
return;
}
// for循环代表树的某一层
for (int i = startIndex; i < nums.length; i++) {
path.add(nums[i]);
// 递归嵌套下一层for
backtracking(nums, i + 1);
// 回溯整理path
path.remove(path.size() - 1);
}
}
90.子集II
大家之前做了 40.组合总和II 和 78.子集 ,本题就是这两道题目的结合,建议自己独立做一做,本题涉及的知识,之前都讲过,没有新内容。题目链接/文章讲解:代码随想录
视频讲解:回溯算法解决子集问题,如何去重?| LeetCode:90.子集II_哔哩哔哩_bilibili
重点:
1. 树层去重
2. 每次递归都把path加入到result
思路:
递归+回溯:
1. 确定参数及返回值
private void backtracking(int[] nums, int startIndex, Boolean[] used)
2. 确定终止条件
其实不写终止条件也可以,因为for循环根本不可能超过nums.length
if (startIndex == nums.length) { return; }
3. 确定单层递归的逻辑
used数组确定是否是树层
for (int i = startIndex; i < nums.length; i++) { if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) { continue; } path.add(nums[i]); used[i] = true; backtracking(nums, i + 1, used); path.remove(path.size() - 1); used[i] = false; }
List<Integer> path;
List<List<Integer>> result;
public List<List<Integer>> subsetsWithDup(int[] nums) {
path = new ArrayList<>();
result = new ArrayList<>();
boolean[] used = new boolean[nums.length];
Arrays.sort(nums);
backtracking(nums, 0, used);
return result;
}
private void backtracking(int[] nums, int startIndex, boolean[] used) {
result.add(new ArrayList<>(path));
if (startIndex == nums.length) {
return;
}
for (int i = startIndex; i < nums.length; i++) {
// nums[i] == nums[i - 1] && used[i - 1] == false意味着在同一层
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
path.add(nums[i]);
used[i] = true;
backtracking(nums, i + 1, used);
path.remove(path.size() - 1);
used[i] = false;
}
}