代码随想录算法训练营第二十八天|93.复原IP地址、78.子集、90.子集II
93.复原IP地址
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。
给定一个只包含数字的字符串 s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s
中插入 '.'
来形成。你 不能 重新排序或删除 s
中的任何数字。你可以按 任何 顺序返回答案。
示例 1:
输入:s = "25525511135"
输出:["255.255.11.135","255.255.111.35"]
题解:分割问题。需要注意的是,分割完需要再字符串中加上点号,并且还要判断每段字符的合法性(单独写一个函数判断)。在回溯的时候,因为添加了一个点号,所以回溯的起始位置是i+2,算上点号占的一个字符。在指定位置添加点号的StringBuilder的方法是.insert(添加位置,添加内容)。
代码:
class Solution {
List<String> res=new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
StringBuilder str=new StringBuilder(s);
backtracking(str,0,0);
return res;
}
public void backtracking(StringBuilder s,int startIndex,int point){
//终止条件
//遍历完字符串,并且用三个点将IP分为4段
if(point==3){
if(isValid(s,startIndex,s.length()-1)){
res.add(s.toString());
}
return;
}
//单层逻辑
for(int i=startIndex;i<s.length();i++){
if(isValid(s,startIndex,i)){
//不能用append(i),它会将点号从开始添加到末尾
s.insert(i+1,'.');
backtracking(s,i+2,point+1);
s.deleteCharAt(i+1);
}else{
break;
}
}
}
//判断是不是合法的IP(每个IP段的值不能为0,并且不能超过255)
public boolean isValid(StringBuilder s,int start,int end){
if(start>end) return false;
//ip段不能为0
if(start!=end && s.charAt(start)=='0') return false;
int sum=0;
for(int i=start;i<=end;i++){
int digit=s.charAt(i)-'0';
sum=sum*10+digit;
if(sum>255)
return false;
}
return true;
}
}
78.子集
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
题解:和组合问题的逻辑是差不多的,区别在于:组合是在叶子节点收获结果,也就是叶子节点是结果,终止条件可以直接判断有没有到叶子结点,并且在终止条件中收获结果集。可是子集是路径上的每一个搜索点都是结果,也就是树层也是结果,那么终止条件该怎么写呢?结果集又该怎么收获呢?
子集问题不在终止条件收获结果集,终止条件就是一条路径已经搜索完毕。
结果集在for循环中收获,每一次回溯到for循环,传入的都是这条路径保存到的当前节点,所以直接将这个path加入到res结果集。
代码:
class Solution {
List<List<Integer>> res=new ArrayList<>();
List<Integer> path=new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backtracking(nums,0);
return res;
}
public void backtracking(int nums[],int startIndex){
res.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.removeLast();
}
}
}
90.子集II
题解:组合和集合结合的问题。需要考虑组合中元素在同一个子集中可以重复出现,但是不能出现重复的子集,所以需要用used数字记录每个数字是否已经被横向遍历过(需要给原数组排序),然后看数组当前值和前一个值是否相等,并判断前一个值是否被遍历过。
子集则是收集结果的时候注意树层的也是结果,终止条件和收集结果的地方需要注意。
代码:
class Solution {
List<List<Integer>> res=new ArrayList<>();
List<Integer> path=new ArrayList<>();
boolean [] used;
public List<List<Integer>> subsetsWithDup(int[] nums) {
used=new boolean[nums.length];
Arrays.fill(used,false);
//给数组排序
Arrays.sort(nums);
backtracking(nums,0);
return res;
}
//回溯 用used数组记录相同元素有没有遍历过
public void backtracking(int[] nums,int startIndex){
//子集,收获结果在回溯开始
res.add(new ArrayList<>(path));
//终止条件
if(startIndex==nums.length) return;
//for循环
for(int i=startIndex;i<nums.length;i++){
if(i>0 && nums[i]==nums[i-1] && !used[i-1]) continue;
path.add(nums[i]);
used[i]=true;
backtracking(nums,i+1);
used[i]=false;
path.removeLast();
}
}
}