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

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

作者思考:

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

递归三部曲

确定递归参数:

由于本题的结果集中是要求我们输出正确的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 ;
}

单层递归逻辑:

//单层递归逻辑
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<String> result = new ArrayList<>();
    public List<String> 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)

作者思考:

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

 递归三部曲

递归参数:

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<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> 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中的元素可以重复,并且我们输出到结果集中的单个集合不能重复。故本题和上一题的大体思路一样,只是多了一个去重操作。

完整代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> 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);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一个只包含正整数的非空数组,是否可以将这个数组分成两个子集,使得两个子集的元素和相等。 示例: 输入:[1, 5, 11, 5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11]。 解题思路: 这是一道经典的 0-1 背包问题,可以使用动态规划或者回溯算法解决。 使用回溯算法,需要定义一个 backtrack 函数,该函数有三个参数: - 数组 nums; - 当前处理到的数组下标 index; - 当前已经选择的元素和 leftSum。 回溯过程中,如果 leftSum 等于数组元素和的一半,那么就可以直接返回 true。如果 leftSum 大于数组元素和的一半,那么就可以直接返回 false。如果 index 到达数组末尾,那么就可以直接返回 false。 否则,就对于当前元素,有选择和不选择两种情况。如果选择当前元素,那么 leftSum 就加上当前元素的值,index 就加 1。如果不选择当前元素,那么 leftSum 不变,index 也加 1。最终返回所有可能性的结果中是否有 true。 Java 代码实现: class Solution { public boolean canPartition(int[] nums) { int sum = 0; for (int num : nums) { sum += num; } if (sum % 2 != 0) { return false; } Arrays.sort(nums); return backtrack(nums, nums.length - 1, sum / 2); } private boolean backtrack(int[] nums, int index, int leftSum) { if (leftSum == 0) { return true; } if (leftSum < 0 || index < 0 || leftSum < nums[index]) { return false; } return backtrack(nums, index - 1, leftSum - nums[index]) || backtrack(nums, index - 1, leftSum); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值