复原id地址
题目:有效 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 {
public List<String> restoreIpAddresses(String s) {
List<String> res = new ArrayList<>();
StringBuilder path = new StringBuilder();
backtracking(s,0,0,res);
return res;
}
private void backtracking(String s,int startIndex,int pointNum,List<String> res){
// 当有三个分割点的时候就是收集结果的时候
if(pointNum == 3){
// 判断第四个数组是否合法
if(isVaild(s,startIndex,s.length() - 1)){
res.add(s);
return;
}
}
for(int i = startIndex;i<s.length();i++){
// 判断当前分割的数字是否合法,如果不合法则跳过
// 此处 i+2 <= s.length() ;是防止字符串下标越界,为什么可以等于字符串的长度,因为下面会加一个“.”,所有当前的i+2在新的字符串中下标一定不会越界
if(isVaild(s,startIndex,i) && i+2 <= s.length()){
s = s.substring(0,i+1) + "." + s.substring(i+1);
pointNum++;
backtracking(s,i+2,pointNum,res);
s = s.substring(0,i+1) + s.substring(i+2);
pointNum--;
}else{
break;
}
}
}
private boolean isVaild(String s,int left,int right){
// 如果第一个数字是0 不合法
if(s.charAt(left) == '0' && left != right){
return false;
}
int num = 0;
for(int i = left;i<=right;i++){
num = num * 10 + (s.charAt(i) - '0');
if(num > 255){
return false;
}
}
return true;
}
}
子集
题目:给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路:与之前的回溯题目有所不同的是本题收集结果的地方不是在叶子节点,而是在每个节点都需要收集
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums.length == 0){
return res;
}
List<Integer> path = new ArrayList<>();
backtracking(nums,0,path,res);
return res;
}
private void backtracking(int[] nums,int startIndex,List<Integer> path,List<List<Integer>> res){
// 每一个节点都需要收集结果
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,res);
path.remove(path.size()-1);
}
}
}
子集||
题目:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路:子集和组合||两者的结合,在每个节点收集结果,并且去掉重复的组合(前提是必须对数组进行排序)
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if(nums.length == 0){
return res;
}
List<Integer> path = new ArrayList<>();
// 标记是否使用过的数组
int[] used = new int[nums.length];
// 去重的前提
Arrays.sort(nums);
backtracking(nums,0,path,res,used);
return res;
}
private void backtracking(int[] nums,int stratIndex,List<Integer> path,List<List<Integer>> res,int[] used){
res.add(new ArrayList<>(path));
if(stratIndex >= nums.length){
return;
}
for(int i = stratIndex;i<nums.length;i++){
// 当前元素和前一个元素相同,并且前一个元素是没有使用过,则跳过
// 为什么是前一个元素没有使用过呢,因为像 1 2 2 这种也是符合条件的,此时第1个2是使用过的,所有如果是没有使用过就说明是递归回溯到上一层,此时第1个2就是没有使用过的状态,但是实际上一个递归已经收集了第1个2的所有集合
if(i > 0 && nums[i] == nums[i - 1] && used[i-1] == 0){
continue;
}
path.add(nums[i]);
used[i] = 1;
backtracking(nums,i+1,path,res,used);
path.remove(path.size() - 1);
used[i] = 0;
}
}
}