代码随想录算法Day28 | 93.复原IP地址, 78.子集 , 90.子集II

93.复原IP地址

题目链接:93. 复原 IP 地址 - 力扣(LeetCode)

思路

复原ip地址,本质上还是切割问题,但是有了不少的条件:

  1. 只能切成4段。
  2. 每一段的长度如果大于1,那么就不能是0开头。如果长度为1,可以是0。
  3. 每一段的数字在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 }

 

posted @ 2023-02-28 23:43  颜欢兮  阅读(21)  评论(0)    收藏  举报