代码随想录算法训练营day28 | 93.复原IP地址,78.子集,90.子集II
93.复原IP地址
1、树的深度为3,可以将分割点的数量作为终止条件;
2、返回的列表中元素是字符串,因此直接在原始字符串上进行修改;
3、startIndex表示分割位置,同时控制横向遍历起始位置;
解法一:回溯
class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
if(s.length()>12)return result;
backtracking(s,0,0);
return result;
}
// startIndex: 搜索的起始位置, pointNum:添加逗点的数量
public void backtracking(String s, int startIndex, int pointNum){
if(pointNum==3){
if(isValid(s,startIndex,s.length()-1)){//判断最后一个子串是否合法
result.add(s);
// System.out.println(s);
}
return;
}
for(int i=startIndex;i<s.length();i++){
//判断子串是否合法
if(isValid(s,startIndex,i)){
s = s.substring(0,i+1)+"."+s.substring(i+1);
System.out.println(s);
pointNum++;
backtracking(s,i+2,pointNum);
//回溯
pointNum--;
s = s.substring(0,i+1)+s.substring(i+2);
}else{
break;
}
}
}
//判断子串是否合法,左闭右闭
public boolean isValid(String s, int start, int end){
//如果第三个逗号加在字符串末尾,则会出现start>=s.length()的情况,因此需要判断start>end
if(start>end){
return false;
}
if(start != end && s.charAt(start)=='0'){//以0开头
return false;
}
int num = 0;
for(int i=start;i<=end;i++){
if(s.charAt(i)<'0' || s.charAt(i)>'9'){//含有非法字符
return false;
}
//不在0~255范围中
num = num * 10 + (s.charAt(i) - '0');
if(num>255){
return false;
}
}
return true;
}
}
78.子集
教程视频:https://www.bilibili.com/video/BV1U84y1q7Ci/?spm_id_from=333.788&vd_source=ddffd51aa532d23e6feac69924e20891
子集问题在每次递归都要收集结果。
解法一:回溯(单独处理空集)
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
result.add(new ArrayList<>(path));
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums,int startIndex){
if(startIndex==nums.length){
return;
}
for(int i=startIndex;i<nums.length;i++){
path.add(nums[i]);
result.add(new ArrayList<>(path));
backtracking(nums,i+1);
path.remove(path.size()-1);
}
}
}
解法二:回溯(统一处理空集)
class Solution {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums,int startIndex){
result.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);
}
}
}
90.子集II
解法一:回溯(与40.组合总和II类似)
需要考虑树层去重和树枝去重
class Solution {
List<List<Integer>> result =new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backtracking(nums,0);
return result;
}
public void backtracking(int[] nums, int startIndex){
result.add(new ArrayList<>(path));
if(startIndex>=nums.length){
return;
}
for(int i=startIndex;i<nums.length; i++){
if(i>startIndex && nums[i]==nums[i-1] )continue;
path.add(nums[i]);
backtracking(nums,i+1);
path.remove(path.size()-1);
}
}
}
总结
子集问题需要在每个节点收集数据,因此result新增元素操作要放在backtracking函数开头.