93.复原IP地址 78.子集 90.子集II
93.复原IP地址
给定一个只包含数字的字符串,复原它并返回所有可能的 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 地址。
示例 1:
- 输入:s = “25525511135”
- 输出:[“255.255.11.135”,“255.255.111.35”]
示例 2:
- 输入:s = “0000”
- 输出:[“0.0.0.0”]
示例 3:
- 输入:s = “1111”
- 输出:[“1.1.1.1”]
示例 4:
- 输入:s = “010010”
- 输出:[“0.10.0.10”,“0.100.1.0”]
示例 5:
- 输入:s = “101023”
- 输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]
提示:
- 0 <= s.length <= 3000
- s 仅由数字组成
思路:回溯法
题目对字符串做分割,找出某种符合条件的组合。相当于遍历一个集合,找出符合条件的组合,可使用回溯法
本题要求字符串分成4段。终止条件可判断分隔符的个数,也可判断递归的深度
当对字符串切割,如果切割的字符串,有效则stringBuilder.append
当递归到第四层时,需要判断stringBuilder是否包含全部字符串字符。因为stringBuilder可能不包含全部的字符串,举例【101023】
1.0.10.2 和 1.0.10.23 第四个元素2和23都是有效的字符,但第一种漏掉了一个元素3,不符合题意,因此要在中止条件做判断并过滤。此题从一个集合中求组合,那么在for循环for(int i = startIndex;i<s.size;i++)中,起始位置为startIndex
集合中元素不允许重复使用,那么每次递归的下一层就是从i+1开始的
回溯法三要素
1.方法入参和返回值。入参:String s,int startIndex 返回值:void
2.中止条件 当遍历层数达到第四层时,判断stringBuilder是否包含全部字符串字符,并中止递归。
3.核心逻辑
递归循环中截取字符串。在for循环for(int i = startIndex;i<s.size;i++)中,起始位置为startIndex
每次切割的字符串范围为【startIndex,i】,然后判断字符串是否为有效IP地址的组成字符串
是有效的字符串,加入stringBuilder.否则break此循环
代码如下
// 时间复杂度O(4 * 2^n) n表示字符串字符个数
// 空间复杂度o(n)
static List<String> result = new ArrayList<>();
static StringBuilder stringBuilder = new StringBuilder();
static int depth = 0;
public static List<String> restoreIpAddresses(String s) {
backTracking(s, 0);
return result;
}
public static void backTracking(String s, int startIndex) {
if (depth >= 4) {
if (stringBuilder.length() - 4 == s.length()) {
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
result.add(stringBuilder.toString());
}
return;
}
for (int i = startIndex; i < s.length(); i++) {
String str = s.substring(startIndex, i + 1);
if (!isVaild(str)) {
break;
}
stringBuilder.append(str).append(".");
depth++;
backTracking(s, i + 1);
depth--;
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
stringBuilder.delete(stringBuilder.lastIndexOf(".") + 1, stringBuilder.length());
}
}
public static boolean isVaild(String str) {
if (str.length() > 1 && str.charAt(0) == '0')
return false;
if (str.length() >= 4)
return false;
if (Integer.parseInt(str) > 255 || Integer.parseInt(str) < 0)
return false;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) > '9' || str.charAt(i) < '0')
return false;
}
return true;
}
78.子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
思路:回溯
此题跟之前回溯题目区别在于
之前题目在中止条件将【子集】加入结果集,本题目每一个子集都是结果,在每一层递归将子集加入结果集
求整数数组的所有子集,子集不能重复。可以用求组合的套路来解
因为子集不能重复,所以for循环就要从startIndex开始
因为元素不能重复多次选择,所以递归的startIndex入参不能等于i,等于i+1
递归法三要素
1.方法入参和返回值。方法入参:int[] nums,int startIndex
2.中止条件。不需要中止条件,当到达一条树枝的终点后,startIndex的值会等于nums.length,不会走for循环,相当于中止条件
3.核心逻辑
递归循环中截取数字。在for循环for(int i = startIndex;i<s.size;i++)中,起始位置为startIndex
取出集合中i对应的数字加并放入path,path加入结果集。递归的startIndex入参不能等于i,等于i+1
将path中最后一个集合去掉即回溯
代码如下
// 时间复杂度O(n * 2^n) n表示字符串字符个数
// 空间复杂度o(n)
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
result.add(new ArrayList<>());
if (nums == null || nums.length == 0) {
return result;
}
backTracking(nums, 0);
return result;
}
public void backTracking(int[] nums, int startIndex) {
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);
}
}
90.子集II
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
- 输入: [1,2,2]
- 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
思路:回溯
思路:回溯
数组中存在重复元素,求数组所有不重复的子集
去重子集最好在遍历过程中,可避免超时
去重步骤
1.先对数组排序
2.for循环遍历时,比较当前结点值和上个结点值,若相同,则跳过此结点
代码如下
//时间复杂度: O(n * 2^n)
//空间复杂度: O(n)
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = 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));
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);
}
}