复原IP地址
原理类似于切割回文串, 也是需要切割
这里的终止条件有点不同, 因为明确规定只能为四段, 所以分割为四段就是终止条件(startIndex其实就是风格先)
- 终止条件, pointSum == 3, 然后对最后一段进行判断isValid(s, startInex, s.size() - 2)左闭右闭
- 单层递归要判断isValid(s, startIndex, i), 这里的区间要注意, start是左区间固定的
- 回溯操作, 插入, 然后pointSum先加,后减, 可以发现其实并不是一个真的逗点
判断isValid的条件:
start > end, return false, 0开头的数字不行(用charAt), 遇到非数字字符不行, 大于255不行
下面直接拉代码, 用一个StringBuilder还是比较方便的
class Solution {
List<String> res = new ArrayList<>();
StringBuilder sb = new StringBuilder();
public List<String> restoreIpAddresses(String s) {
sb.append(s);
backtracking(sb, 0, 0);
return res;
}
public void backtracking(StringBuilder sb, int start, int pointNum){
if(pointNum == 3){
if(isValid(sb, start, sb.length() - 1)){
res.add(sb.toString());
return;
}
}
for(int i = start; i < sb.length(); i++){
//注意这里的左右区间。 start不变, i是会变的
if(isValid(sb, start, i)){
//这个方法实在i + 1的位置加上'.'
sb.insert(i + 1, '.');
pointNum++;
//这里回溯要写i + 2, 因为他加上一个字符一个点是2,end注意是pointNum
backtracking(sb, i + 2, pointNum);
pointNum--;
//这里回溯撤回i + 1, 也就是.的位置
sb.deleteCharAt(i + 1);
} else {
break;
}
}
}
public boolean isValid(StringBuilder sb, int start, int end){
if(start > end){
return false;
}
//判断开头字母不能为0
if(sb.charAt(start) == '0' && start != end){
return false;
}
//判断非数字字符也not valid
int num = 0;
//这里的便利要注意, 左右边界都会改变的
for(int i = start; i <= end; i++){
if(sb.charAt(i) < '0' || sb.charAt(i) > '9'){
return false;
}
num = num*10 + (sb.charAt(i) - '0');
if(num > 255){
return false;
}
}
return true;
}
}
子集
(给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集)
组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
剩余集合为空的时候,就是叶子节点, 也就是startIndex≥nums.length时, 说明全部元素都取完了
细节:
- 这里的收集子集不需要进入if判断, 而是每一个节点都需要收集
- 这里的判断终止条件就是start大于等于长度了, 说明都找完了
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums, 0);
return res;
}
public void backtracking(int[]nums, int start){
//遍历的时候要把每个节点都记下来, 而不是到终止条件才记
res.add(new ArrayList<>(path));
//唯一的区别就是这里的终止条件,找完所有元素就结束
if(start >= nums.length){
return;
}
for(int i = start; i < nums.length; i++){
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
子集II
和上一题的区别就是, 数组里和会有重复, 而解集不能重复
而这里的去重逻辑和之前的组合总和II一模一样, 就是判断树层
class Solution {
List<Integer> path = new ArrayList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {\
//别忘记也要sort
Arrays.sort(nums);
backtracking(nums, 0);
return res;
}
private void backtracking(int[] nums, int start){
res.add(new ArrayList<>(path));
if(start >= nums.length){
return;
}
for(int i = start; i < nums.length; i++){
//和之前的组合总和II同理,去重
if(i > start && nums[i] == nums[i - 1]){
continue;
}
path.add(nums[i]);
backtracking(nums, i + 1);
path.remove(path.size() - 1);
}
}
}