目录
93. 复原 IP 地址
难度:medium
类型:回溯,切割
思路:
切割问题类似于组合问题;
需要注意的事,我们使用count来作为递归终止条件,count统计的事“.”的个数,每个IP地址有四个数,当count等于3时,如果最后一个数也符合回文条件,则是合规的IP地址。count等于3时,递归终止。
代码:
class Solution {
private List<String> ans = new ArrayList<>();
// 对小圆点进行计数
private int count = 0;
public List<String> restoreIpAddresses(String s) {
if (s == null || s.length() == 0) {
return ans;
}
backtracking(s, 0);
return ans;
}
public void backtracking(String s, int startIndex) {
if (count == 3) {
if (judge(s, startIndex, s.length())) {
ans.add(s);
}
return;
}
for (int i = startIndex; i < s.length(); i++) {
if (judge(s, startIndex, i + 1)) {
// [startIndex, i + 1)符合要求,[0, startIndex - 1)已经验证过符合IP地址
// [i + 1, s.length())还没有验证,递归给下一层进行验证
s = s.substring(0, i + 1) + "." + s.substring(i + 1, s.length());
count++;
// 添加了原点所以i+1+1
backtracking(s, i + 2);
s = s.substring(0, i + 1) + s.substring(i + 2, s.length());
count--;
} else {
break;
}
}
}
// 每个整数位于 0 到 255 之间组成,且不能含有前导 0
// 左闭右开
public boolean judge(String str, int start, int end) {
// i + 2的隐患
if (start >= end) {
return false;
}
// 前导0
if (str.charAt(start) == '0' && end > start + 1) {
return false;
}
// 每个整数位于 0 到 255 之间组成
int sum = 0;
for (int i = start; i < end; i++) {
if (str.charAt(i) < '0' || str.charAt(i) > '9') {
return false;
}
sum = sum * 10 + (str.charAt(i) - '0');
if (sum > 255) {
return false;
}
}
return true;
}
}
- 时间复杂度: O(n * 2^n)
- 空间复杂度: O(n^2)
78. 子集
难度:medium
类型:回溯,子集
思路:
组合问题是收集递归树的叶子节点,子集问题就是收集递归树的每一个节点;
本题nums没有重复元素,所以不用排序和去重。
代码:
// 子集问题需要收集递归树的每一个节点
class Solution {
private List<List<Integer>> list = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums, 0);
return list;
}
public void backtracking(int[] nums, int startIndex) {
// 记录所有节点,包括空集
list.add(new ArrayList<>(path));
// 终止条件可以不写
if (startIndex == nums.length) {
return;
}
for (int i = startIndex; i < nums.length; i++) {
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
- 时间复杂度: O(n * 2^n)
- 空间复杂度: O(n)
90. 子集 II
难度:medium
类型:回溯,子集
思路:
这道题是 40.组合总和II 和 78.子集 的结合,结合了子集问题和去重方法;
子集问题:收集递归树的每一个节点
去重方法:同 40.组合总和II,可以使用used数组也可以不用。代码随想录算法训练营day27 | 39. 组合总和,40. 组合总和 II,131. 分割回文串_Chamberlain T的博客-CSDN博客
本题也可以不使用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。
如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。
代码:
class Solution {
private List<List<Integer>> ans = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backtracking(nums, 0);
return ans;
}
public void backtracking(int[] nums, int startIndex) {
ans.add(new ArrayList<>(path));
if (startIndex >= nums.length) {
return;
}
for (int i = startIndex; i < nums.length; i++) {
// 去重,操作同 40. 组合总和 II
if (i > startIndex && nums[i] == nums[i - 1]) {
continue;
}
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
- 时间复杂度: O(n * 2^n)
- 空间复杂度: O(n)