leetcode-zgd-day28-93.复原IP地址/78.子集/90.子集II

144 阅读2分钟

93.复原IP地址

题目链接:Loading Question... - 力扣(LeetCode)

解题思路:

错误代码:

差别主要在于if(s.charAt(startIndex) == '0')下面的那层for循环是否还需要套一层else。因为想到了平时if else语句,通常可以将else省略,直接写逻辑,因为不符合else的条件在if中肯定被拦截走并处理掉了,能够执行else部分的语句,肯定是符合要求的语句。但是,题目中并非这种情况。因为if语句的执行并没有改变任何事情,因为是回溯法。所以当下面的for循环不加else的时候,前面的if语句执行完了还会继续执行下面的for循环。这样if语句就完全没有起到应起的作用。

 class Solution {
 ​
     List<String> ans = new ArrayList<>();
     List<String> path = new LinkedList<>();
 ​
     public List<String> restoreIpAddresses(String s) {
         backTracking(s, 0);
         return ans;
     }
     public void backTracking(String s, int startIndex){
         // 主要任务就是拆分字符串,点可以最后再添加
         // 结束条件
         if(path.size() == 4 && startIndex == s.length()){ // 四段数字并且字符串已经完全分割完了
 //          ans.add(StringUtils.join(path.toArray(),"."));
             ans.add(path.stream().collect(Collectors.joining(".")));
             return;
         }
         if(startIndex >= s.length()) return;
         if(s.charAt(startIndex) == '0'){
             path.add(s.substring(startIndex, startIndex + 1));
             backTracking(s, startIndex + 1);
             path.remove(path.size() - 1);
         }
         for(int i = startIndex + 1; i <= s.length(); i++){
             int ip = Integer.valueOf(s.substring(startIndex, i));
             if(ip < 0 || ip > 255) break;
             path.add(s.substring(startIndex, i));
             backTracking(s, i);
             path.remove(path.size() - 1);
         }
     }
 }

所以需要外套一层else语句。这样才能够保证水平遍历的时候分割的第一个数字不为0

纠正后的代码:

 class Solution {
 ​
     List<String> ans = new ArrayList<>();
     List<String> path = new LinkedList<>();
 ​
     public List<String> restoreIpAddresses(String s) {
         backTracking(s, 0);
         return ans;
     }
     public void backTracking(String s, int startIndex){
         // 主要任务就是拆分字符串,点可以最后再添加
         // 结束条件
         if(path.size() == 4 && startIndex == s.length()){ // 四段数字并且字符串已经完全分割完了
 //          ans.add(StringUtils.join(path.toArray(),"."));
             ans.add(path.stream().collect(Collectors.joining(".")));
             return;
         }
         if(startIndex >= s.length()) return;
         if(s.charAt(startIndex) == '0'){
             path.add(s.substring(startIndex, startIndex + 1));
             backTracking(s, startIndex + 1);
             path.remove(path.size() - 1);
         }
         else{ // 这里必须包这层else
             for(int i = startIndex + 1; i <= s.length(); i++){
                 int ip = Integer.valueOf(s.substring(startIndex, i));
                 if(ip < 0 || ip > 255) break;
                 path.add(s.substring(startIndex, i));
                 backTracking(s, i);
                 path.remove(path.size() - 1);
             }
         }
     }
 }

78.子集

题目链接:Loading Question... - 力扣(LeetCode)

解题思路:

子集问题和组合问题的区别在于,前面做的两个组合问题都是要求输出固定长度的结果,所以在终止条件处要添加判断终止长度的代码,以确定当前的组合是否符合要求。不过子集问题并不是这样,子集问题需要输出在回溯过程中path在更新过程中的所有路径。

 class Solution {
 ​
     List<List<Integer>> ans = new ArrayList<>();
     List<Integer> path = new LinkedList<>();
 ​
 ​
     public List<List<Integer>> subsets(int[] nums) {
         backTracking(nums, 0);
         return ans;
     }
 ​
     public void backTracking(int[] nums, int startIndex){
         // 终止条件
         if(startIndex > nums.length) return;
         ans.add(new ArrayList<>(path));
         for(int i = startIndex; i < nums.length; i++){
             path.add(nums[i]);
             backTracking(nums, i + 1);
             path.remove(path.size() - 1);
         }
     }
 }

代码中ans.add(path)的位置比较值得思考,终止条件如果时startIndex == nums.length,那么就必须把add语句放在if条件判断的前面,否则所有带最后一个数字的子集就都在add之前就返回了。但是这样其实并不那么好理解,最好是上来先将记录插入,然后再判断当前是否可以结束。代码如下:

 class Solution {
 ​
     List<List<Integer>> ans = new ArrayList<>();
     List<Integer> path = new LinkedList<>();
 ​
 ​
     public List<List<Integer>> subsets(int[] nums) {
         backTracking(nums, 0);
         return ans;
     }
 ​
     public void backTracking(int[] nums, int startIndex){
         ans.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.remove(path.size() - 1);
         }
     }
 }

将add语句放到前面就可以放心的使用startIndex == nums.length这个条件了。

90.子集II

题目链接:Loading Question... - 力扣(LeetCode)

解题思路:

和子集I的区别在于,该题目中出现了重复的元素,而且不允许子集出现重复,所以就需要对重复解进行剪枝。所以用到了used数组。这里回溯的过程中不要忘记更改used数组的状态。

 class Solution {
 ​
     List<List<Integer>> ans = new ArrayList<>();
     List<Integer> path = new LinkedList<>();
     boolean[] used;
 ​
     public List<List<Integer>> subsetsWithDup(int[] nums) {
         used = new boolean[nums.length];
         Arrays.fill(used,false);
         Arrays.sort(nums);
         backTracking(nums, used, 0);
         return ans;
     }
     public void backTracking(int[] nums, boolean[] used, int startIndex){
         ans.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] == false){
                 continue;
             }
             path.add(nums[i]);
             used[i] = true;
             backTracking(nums, used, i + 1);
             used[i] = false;
             path.remove(path.size() - 1);
         }
     }
 }