代码随想录算法训练营第28天 | LeetCode93.复原IP地址、LeetCode78.子集、LeetCode90.子集II

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

作者思考:

本题和LeetCode131.分割回文串都是属于切割字符串问题中,比较复杂的题目。本题的难点:如何模拟切割的操作、如何找到切割点、判断子串是否是合法的IP地址、如何将合法的子串拼接。

代码随想录算法训练营第28天 | LeetCode93.复原IP地址、LeetCode78.子集、LeetCode90.子集II_第1张图片

递归三部曲

确定递归参数:

由于本题的结果集中是要求我们输出正确的IP地址,这时就要求我们在切割出正确的字符串子串后,需要对原字符串进行添加“.”操作;如果按照上述说法,每次切割出正确的都添加“.”看起来好像没有什么问题,但是当我们切完整个字符串后,会发现字符串的最后多一个“.”,此时就需要我们控制添加次数,当我们添加三次后,直接判断剩余的字符是否符合正确的IP地址的格式。

void backtracking(String s, int startindex, int pointsum) {

}

确定终止条件:

上文提到了,当我们的逗点个数等于三,直接判断剩余的字符是否符合正确的IP地址的格式。

if (pointsum == 3) {
    if (trueIp(s, startindex, s.length()-1)) {
        result.add(s);
    }
    return ;
}

单层递归逻辑:

代码随想录算法训练营第28天 | LeetCode93.复原IP地址、LeetCode78.子集、LeetCode90.子集II_第2张图片

//单层递归逻辑
for (int i = startindex; i < s.length(); i++) {
    if (trueIp(s, startindex, i)) {//当前的子串符合ip的规范,进行插入.
        s = s.substring(0, i+1)+'.'+s.substring(i + 1, s.length());
        pointsum++;
        backtracking(s, i+2, pointsum);
        s = s.substring(0, i+1) + s.substring(i + 2, s.length());
        pointsum--;
     }else {
         break ;
     }
}  

判断IP地址是否合法:

//判断字符串s在区间左闭右闭所组成的数字是否合法
boolean trueIp(String s, int start, int end) {//判断是否是合法ip地址
       //合法的区间
       if (start > end) {
            return false;
        }
        //子串不能含有前导0(子串的长度不为1)
        if (s.charAt(start) == '0' && start != end) {
            return false;
        }
        int sum = 0;
        for (int i = start; i <= end; i++) {
            //子串中的字符 要在0-9之间
            if (s.charAt(i) < '0' || s.charAt(i) > '9') {
                return false;
            }
            //子串中最大不超过255
            sum = sum * 10 + (s.charAt(i) - '0'); 
            if (sum > 255) {
                return false;
            }
        }
        return true;
}

 完整代码:

class Solution {
    List result = new ArrayList<>();
    public List restoreIpAddresses(String s) {
        if (s.length() > 12 || s.length() < 4) {
            return result;
        }
        backtracking(s, 0, 0);
        return result;
    }
    void backtracking(String s, int startindex, int pointsum) {
        if (pointsum == 3) {
            if (trueIp(s, startindex, s.length()-1)) {
                result.add(s);
            }
            return ;
        }
        //单层递归逻辑
        for (int i = startindex; i < s.length(); i++) {
            if (trueIp(s, startindex, i)) {//当前的子串符合ip的规范,进行插入.
                s = s.substring(0, i+1)+'.'+s.substring(i + 1, s.length());
                pointsum++;
                backtracking(s, i+2, pointsum);
                s = s.substring(0, i+1) + s.substring(i + 2, s.length());
                pointsum--;
            }else {
                break ;
            }
        }  
    }
    //判断字符串s在区间左闭右闭所组成的数字是否合法
    boolean trueIp(String s, int start, int end) {//判断是否是合法ip地址
        //合法的区间
        if (start > end) {
            return false;
        }
        //子串不能含有前导0(子串的长度不为1)
        if (s.charAt(start) == '0' && start != end) {
            return false;
        }
        int sum = 0;
        for (int i = start; i <= end; i++) {
            //子串中的字符 要在0-9之间
            if (s.charAt(i) < '0' || s.charAt(i) > '9') {
                return false;
            }
            //子串中最大不超过255
            sum = sum * 10 + (s.charAt(i) - '0'); 
            if (sum > 255) {
                return false;
            }
        }
        return true;
    }
}

 题目链接:78. 子集 - 力扣(LeetCode)

作者思考:

之前的组合问题,其实都是收集抽象树中的叶子节点,那本题是要求我们返回所有的子集,就是找抽象树的所有节点。

代码随想录算法训练营第28天 | LeetCode93.复原IP地址、LeetCode78.子集、LeetCode90.子集II_第3张图片

 递归三部曲

递归参数:

startindex 用来说明下一次取元素的位置

void backtracking(int[] nums, int startindex) {
}

终止条件:

因为本题是收集树中的所有节点,我们每一次递归都要收集。当startindex >= nums.length时整个数组都被遍历完。

result.add(new ArrayList<>(path));
if (startindex >= nums.length) {
    return ;
}

 单层递归逻辑:

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.remove(path.size() -1);
   }
}

完整代码:

 

class Solution {
    List> result = new ArrayList<>();
    List path = new ArrayList<>();
    public List> subsets(int[] nums) {
        backtracking(nums, 0);
        return result;
    }
    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.remove(path.size() -1);
        }
    }
}

题目链接:90. 子集 II - 力扣(LeetCode)

作者思考:

本题和LeetCode78.子集的区别是,本题所给定的整数数组nums中的元素可以重复,并且我们输出到结果集中的单个集合不能重复。故本题和上一题的大体思路一样,只是多了一个去重操作。

代码随想录算法训练营第28天 | LeetCode93.复原IP地址、LeetCode78.子集、LeetCode90.子集II_第4张图片

完整代码:

class Solution {
    List> result = new ArrayList<>();
    List path = new ArrayList<>();
    public List> subsetsWithDup(int[] nums) {
        //因为给定的集合不一定是有序的,本题涉及到去重,先将数组中的元素用库函数去重
        Arrays.sort(nums);
        backtracking(nums, 0);
        return result;
    }
    void backtracking(int[] nums, int startindex) {
        result.add(new ArrayList<>(path));
        if (startindex >= nums.length) {
            return ;
        }
        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);
        }
    }
}

你可能感兴趣的:(leetcode,算法)