代码随想录算法Day28 | 93.复原IP地址, 78.子集 , 90.子集II
93.复原IP地址
题目链接:93. 复原 IP 地址 - 力扣(LeetCode)
思路
复原ip地址,本质上还是切割问题,但是有了不少的条件:
- 只能切成4段。
- 每一段的长度如果大于1,那么就不能是0开头。如果长度为1,可以是0。
- 每一段的数字在0~255之间。
于是我们对之前切割回文的代码进行修改,满足上面三个条件。具体写法是:
- 当切割到字符串的末尾,且切成4段时,加入结果集。
- 因为每段的长度在1到3之间,所以遍历切割位置时,从startIndex开始,切到startIndex + 3。
- 取当前字符串,判断是否出现了0(长度大于1的情况下),是否在0~255之间。
- 如果没有问题,就加入path,然后递归,递归结束后回溯。
详细如图所示。

代码
1 class Solution { 2 List<String> result = new ArrayList<>(); 3 4 public List<String> restoreIpAddresses(String s) { 5 if (s.length() > 12) return result; // 算是剪枝了 6 backTrack(s, 0, 0); 7 return result; 8 } 9 10 // startIndex: 搜索的起始位置, pointNum:添加逗点的数量 11 private void backTrack(String s, int startIndex, int pointNum) { 12 if (pointNum == 3) {// 逗点数量为3时,分隔结束 13 // 判断第四段⼦字符串是否合法,如果合法就放进result中 14 if (isValid(s,startIndex,s.length()-1)) { 15 result.add(s); 16 } 17 return; 18 } 19 for (int i = startIndex; i < s.length(); i++) { 20 if (isValid(s, startIndex, i)) { 21 s = s.substring(0, i + 1) + "." + s.substring(i + 1); //在str的后⾯插⼊⼀个逗点 22 pointNum++; 23 backTrack(s, i + 2, pointNum);// 插⼊逗点之后下⼀个⼦串的起始位置为i+2 24 pointNum--;// 回溯 25 s = s.substring(0, i + 1) + s.substring(i + 2);// 回溯删掉逗点 26 } else { 27 break; 28 } 29 } 30 } 31 32 // 判断字符串s在左闭⼜闭区间[start, end]所组成的数字是否合法 33 private Boolean isValid(String s, int start, int end) { 34 if (start > end) { 35 return false; 36 } 37 if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法 38 return false; 39 } 40 int num = 0; 41 for (int i = start; i <= end; i++) { 42 if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 遇到⾮数字字符不合法 43 return false; 44 } 45 num = num * 10 + (s.charAt(i) - '0'); 46 if (num > 255) { // 如果⼤于255了不合法 47 return false; 48 } 49 } 50 return true; 51 } 52 } 53 54 //方法二:比上面的方法时间复杂度低,更好地剪枝,优化时间复杂度 55 class Solution { 56 List<String> result = new ArrayList<String>(); 57 StringBuilder stringBuilder = new StringBuilder(); 58 59 public List<String> restoreIpAddresses(String s) { 60 restoreIpAddressesHandler(s, 0, 0); 61 return result; 62 } 63 64 // number表示stringbuilder中ip段的数量 65 public void restoreIpAddressesHandler(String s, int start, int number) { 66 // 如果start等于s的长度并且ip段的数量是4,则加入结果集,并返回 67 if (start == s.length() && number == 4) { 68 result.add(stringBuilder.toString()); 69 return; 70 } 71 // 如果start等于s的长度但是ip段的数量不为4,或者ip段的数量为4但是start小于s的长度,则直接返回 72 if (start == s.length() || number == 4) { 73 return; 74 } 75 // 剪枝:ip段的长度最大是3,并且ip段处于[0,255] 76 for (int i = start; i < s.length() && i - start < 3 && Integer.parseInt(s.substring(start, i + 1)) >= 0 77 && Integer.parseInt(s.substring(start, i + 1)) <= 255; i++) { 78 // 如果ip段的长度大于1,并且第一位为0的话,continue 79 if (i + 1 - start > 1 && s.charAt(start) - '0' == 0) { 80 continue; 81 } 82 stringBuilder.append(s.substring(start, i + 1)); 83 // 当stringBuilder里的网段数量小于3时,才会加点;如果等于3,说明已经有3段了,最后一段不需要再加点 84 if (number < 3) { 85 stringBuilder.append("."); 86 } 87 number++; 88 restoreIpAddressesHandler(s, i + 1, number); 89 number--; 90 // 删除当前stringBuilder最后一个网段,注意考虑点的数量的问题 91 // i + number + 2 是因为先加原先的小数点的数量,原本为i + number + 1 不能忘记后续又加了个小数点 所以要在加 1 92 stringBuilder.delete(start + number, i + number + 2); 93 } 94 } 95 }
78.子集
题目链接:78. 子集 - 力扣(LeetCode)
思路
这道题和组合思路是差不多的,只是组合是加入叶子节点到结果集,而这道题需要吧所有节点加入结果集。
详细如图所示。

代码
1 class Solution { 2 List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合 3 LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果 4 public List<List<Integer>> subsets(int[] nums) { 5 subsetsHelper(nums, 0); 6 return result; 7 } 8 9 private void subsetsHelper(int[] nums, int startIndex){ 10 result.add(new ArrayList<>(path));//「遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合」。 11 if (startIndex >= nums.length){ //终止条件可不加 12 return; 13 } 14 for (int i = startIndex; i < nums.length; i++){ 15 path.add(nums[i]); 16 subsetsHelper(nums, i + 1); 17 path.removeLast(); 18 } 19 } 20 }
90.子集II
题目链接:90. 子集 II - 力扣(LeetCode)
思路
这道题和上一题不同在于有集合里有重复元素了,而且求取的子集要去重。
去重思路和 40.组合II 一致。
详细如图所示。

注意,要先进行排序,使相邻元素聚集在一起在进行去重。
代码
1 // 使用used 数组 2 class Solution { 3 List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合 4 LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果 5 boolean[] used; 6 public List<List<Integer>> subsetsWithDup(int[] nums) { 7 if (nums.length == 0){ 8 result.add(path); 9 return result; 10 } 11 Arrays.sort(nums); 12 used = new boolean[nums.length]; 13 subsetsWithDupHelper(nums, 0); 14 return result; 15 } 16 17 private void subsetsWithDupHelper(int[] nums, int startIndex){ 18 result.add(new ArrayList<>(path)); 19 if (startIndex >= nums.length){ 20 return; 21 } 22 for (int i = startIndex; i < nums.length; i++){ 23 if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){ 24 continue; 25 } 26 path.add(nums[i]); 27 used[i] = true; 28 subsetsWithDupHelper(nums, i + 1); 29 path.removeLast(); 30 used[i] = false; 31 } 32 } 33 }
1 // 不使用 used 数组 2 class Solution { 3 4 List<List<Integer>> res = new ArrayList<>(); 5 LinkedList<Integer> path = new LinkedList<>(); 6 7 public List<List<Integer>> subsetsWithDup( int[] nums ) { 8 Arrays.sort( nums ); 9 subsetsWithDupHelper( nums, 0 ); 10 return res; 11 } 12 13 14 private void subsetsWithDupHelper( int[] nums, int start ) { 15 res.add( new ArrayList<>( path ) ); 16 17 for ( int i = start; i < nums.length; i++ ) { 18 // 跳过当前树层使用过的、相同的元素 19 if ( i > start && nums[i - 1] == nums[i] ) { 20 continue; 21 } 22 path.add( nums[i] ); 23 subsetsWithDupHelper( nums, i + 1 ); 24 path.removeLast(); 25 } 26 } 27 28 }

浙公网安备 33010602011771号