给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “[email protected]” 是 无效的 IP 地址。
1,递归函数中传入startIndex代表切割的位置,dots代表点的个数,当点的个数为3且最后一个分割出的字符串也合法时就收集结果。其实终止条件就是树的深度,此题中树的深度为3(只能切割3次)。
2,本题中的一个重难点在于子串的区间,其实可以这样想,将startIndex固定,i从startIndex开始往后移,每移一个位置就要判断一次合法性,因此区间应为[startIndex, i]。
3,本题没有新建path变量来记录每个递归树,而是直接在原字符上进行操作的,因此在单层递归逻辑中找到一个合法子串加上逗点以后再继续递归时传入的startIndex应该是i+2。
class Solution {
List<String> result = new ArrayList<>();
public List<String> restoreIpAddresses(String s) {
backTracking(s, 0, 0);
return result;
}
//startIndex 代表切割线
public void backTracking(String s, int startIndex, int dots){
//dots代表字符串中添加的点的个数,当点有3个时,判断一下分割的最后一个字符串是否合法,如果合法就收集结果。
if(dots == 3){
if(isValid(s, startIndex, s.length() - 1)){
result.add(s);
return;
}
}
for(int i = startIndex; i < s.length(); i++){
//找到判断的区间也是本题的一个重难点,startIndex表示当前切割线,i从当前一直往后挪动。
if(isValid(s, startIndex, i)){
//如果找到合法的打点的位置,在i后面打个点。
s = s.substring(0, i + 1) + "." + s.substring(i + 1);
dots++;
backTracking(s, i + 2, dots); // 递归应该从i+2开始,因为i+1的位置已经是点了。
//回溯,删掉点
s = s.substring(0, i + 1) + s.substring(i + 2);
dots--;
} else {
break; //如果遇到不合法的字符串,直接跳出
}
}
}
public boolean isValid(String s, int start, int end){
if(start > end) return false;
//0开头且长度大于1,不合法
if(s.charAt(start) == '0' && end > start) return false;
int sum = 0;
for(int i = start; i <= end; i++){
if(s.charAt(i) > '9' || s.charAt(i) < '0') return false;
sum = sum * 10 + (s.charAt(i) - '0'); //将字符转换为数字。
if(sum > 255) return false;
}
return true;
}
}
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
1,本题是求所有的子集,与组合问题和切割问题不同的是本题收获的是所有树形结构的节点,而组合问题和切割问题收获的是叶子节点。
2, 因此在每层递归开始时就需要收集结果,而不是在终止条件下收集。
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> result = 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.removeLast();
}
}
}
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
1,自己独立写出来了这题,与组合中有重复的问题类似,引入一个used数组来记录数组中的值是否被用过。
2,另外需要注意本题要先将原数组进行排序,这样才能保证相同的元素出现在一起,这样遇到相同的元素就跳过。
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> result = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
//注意要先将数组排序,这样才能使相同的元素在一起。
Arrays.sort(nums);
int[] used = new int[nums.length];
backTracking(nums, 0, used);
return result;
}
public void backTracking(int[] nums, int startIndex, int[] used){
result.add(new ArrayList<>(path));
if(startIndex >= nums.length) return;
for(int i = startIndex; i < nums.length; i++){
if(i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0){
continue;
}
path.add(nums[i]);
used[i] = 1;
backTracking(nums, i + 1, used);
path.removeLast();
used[i] = 0;
}
}
}