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 中的任何数字。你可以按 任何 顺序返回答案。
回溯
class Solution {
// 存放结果集
List<String> res = new ArrayList<>();
// 拼接字符串
StringBuilder strb = new StringBuilder();
public List<String> restoreIpAddresses(String s) {
restoreIpAddresses(s, 0, 0);
return res;
}
// start:确保s的数字全部读取完
// number:确保ip分割为4段
public void restoreIpAddresses(String s, int start, int number){
// 当start == s的长度时,表示已经全部读取完毕
// number == 4,表示ip被分为了4段,符合ip的规格要求
if(s.length() == start && number == 4){
res.add(strb.toString());
return;
}
// s.length() == start || number == 4,只达到其中一个要求,并不算符合条件,直接返回
if(s.length() == start || number == 4){
return;
}
// 确保ip字段每个的长度不超过3位,并且保证字段在[0, 255]之间
for(int i = start; i < s.length() && i - start < 3 && Integer.parseInt(s.substring(start, i + 1)) >= 0 && Integer.parseInt(s.substring(start, i + 1)) <= 255; i++){
// ip的长度大于1并且第一个数为“0”的话,则continue
if(i + 1 - start > 1 && s.charAt(start) - '0' == 0){
continue;
}
// 拼接字段
strb.append(s.substring(start, i + 1));
// 当ip的网段小于3时,才需要加“.”,如果等于3,则表示已经有3个网段了,则第4个网段后尾不需要加“.”
if(number < 3){
strb.append(".");
}
number++;
restoreIpAddresses(s, i + 1, number);
number--;
// 删除strb最后一个网段
strb.delete(start + number, i + number + 2);
}
}
}
78.子集
题目
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
回溯
class Solution {
// 存放结果集
List<List<Integer>> res = new ArrayList<>();
// 收集符合条件的结果
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsets(int[] nums) {
subsets(nums, 0);
return res;
}
// startIndex:用来将元素进行子集的拼接操作
public void subsets(int[] nums, int startIndex){
// 为符合条件的结果,创建节点
// 放在终止条件的上面是为了将[1,2,3]存入
res.add(new ArrayList<>(path));
// 终止条件
if(startIndex >= nums.length) return;
for(int i = startIndex; i < nums.length; i++){
path.add(nums[i]);
subsets(nums, i + 1);
path.removeLast(); // 回溯
}
}
}
78.子集II
题目
给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
回溯(有used)
class Solution {
// 存放结果集
List<List<Integer>> res = new ArrayList<>();
// 收集符合条件的结果
LinkedList<Integer> path = new LinkedList<>();
boolean[] used;
public List<List<Integer>> subsetsWithDup(int[] nums) {
// 如果nums为空时,将path(此时path为空)加入
if(nums.length == 0){
res.add(path);
return res;
}
// 排序,为后面去重做准备
Arrays.sort(nums);
used = new boolean[nums.length];
subsetsWithDup(nums, 0);
return res;
}
// startIndex:用来将元素进行子集的拼接操作
public void subsetsWithDup(int[] nums, int startIndex){
// 为符合条件的结果,创建节点
// 放在终止条件的上面是为了将[1,2,2]存入
res.add(new ArrayList<>(path));
// 终止条件
if(startIndex >= nums.length) return;
for(int i = startIndex; i < nums.length; i++){
// 确保在下标位置不为0,前一个与后一个相同并且不为空的情况,则continue
if(i > startIndex && nums[i] == nums[i - 1] && !used[i - 1]) continue;
path.add(nums[i]);
used[i] = true;
subsetsWithDup(nums, i + 1);
path.removeLast(); // 回溯
used[i] = false; // 回溯
}
}
}
回溯(没有used)
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
subsetsWithDup(nums, 0);
return res;
}
public void subsetsWithDup(int[] nums, int startIndex){
res.add(new ArrayList<>(path));
for(int i = startIndex; i < nums.length; i++){
if(i > startIndex && nums[i] == nums[i - 1]) continue;
path.add(nums[i]);
subsetsWithDup(nums, i + 1);
path.removeLast();
}
}
}