day24 | lc93复制ip地址 | lc78子集 | lc90子集II

day24

lc93_复原IP地址

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

本题不仅要对输入的数进行切割 还要对切割的数进行合法性检查

什么是不合法的ip呢?

转换成n叉树

难点:

  1. 切割的子串怎么表达范围呢

使用startIndex 最后一个子串的范围极速 [start, s.length() - 1]

  1. 合法性怎么判断
    • 数字前面不能有0 (可以只有一个0 ,但是0不能作为开头)
    • 每个范围小于255
    • 得是正整数字符
  2. 切割线怎么模拟

在代码中 startIndex就是分割线


回溯三部曲:

  1. 回溯函数的参数, 返回值
    • void backtracking()
    • 参数 : 结果集res, 小数点个数pointSum 起始位置startIndex
  2. 终止条件
    • 小数点数量等于3个 并且最后一个子串合法 加入结果集 然后return
  3. 单层递归回溯逻辑
    • 合法性判断: for循环之后 对切割的子串就要合法性判断 合法才进行下去 子串范围是[sIndex , i ]
    • 改造字符串s : 插入小数点 插入位置为 开始位置+i + 1 然后小数点数量加1
    • 开始递归: 起始位置变成i + 2 因为还要算上插入的小数点
    • 回溯 : 删除插入的小数点 小数点数量减1;
class Solution {
    public List<String> restoreIpAddresses(String s) {
        List<String> res = new ArrayList<>();
        StringBuilder sb = new StringBuilder(s); // 初始化 StringBuilder
        backtracking(sb, res, 0, 0); 
        return res;
    }

    public void backtracking(StringBuilder s, List<String> res, int pointSum, int sIndex) {
        // 如果已经插入3个点,检查最后一段是否合法
        if (pointSum == 3 && check(s, sIndex, s.length() - 1)) {
            res.add(s.toString()); // 添加合法结果
            return;
        }

        for (int i = sIndex; i < s.length(); i++) {
            if (check(s, sIndex, i)) { // 检查当前段是否合法
                s.insert(i + 1, '.'); // 插入.点
                backtracking(s, res, pointSum + 1, i + 2); // 递归调用
                s.deleteCharAt(i + 1); // 回溯,删除.点
            } else {
                break; // 如果当前段不合法,直接退出
            }
        }
    }
     public boolean check(StringBuilder s, int l, int r) {
        if (l > r) return false; // 起始位置大于结束位置
        if (s.charAt(l) == '0' && l != r) return false; // 前导0不合法
        int size = 0;
        for (int i = l; i <= r; i++) {
            int digit = s.charAt(i) - '0'; // 将字符转为数字
            size = size * 10 + digit;
            if (size > 255) return false; // 超过255不合法
        }
        return true;
    }
}

lc78_子集(不重复)

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

子集是收集树形结构中树的所有节点的结果

而组合问题、分割问题是收集树形结构中叶子节点的结果


套模板得出代码

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        backtracking(nums, 0, res, path);
        return res;
        
    }
    public void backtracking(int[] arr, int sIndex, List<List<Integer>> res, List<Integer> path){
        res.add(new ArrayList<>(path));
        if (sIndex >= nums.length) return;
        
        for(int i = sIndex; i < arr.length; i++){
            path.add(arr[i]);
            backtracking(arr, i + 1, res, path);
            path.remove(path.size() - 1);
        }
    }
}

lc90_子集II(重复)

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

  • 输入: [1,2,2]
  • 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]

这道题目和lc78的区别就是集合里有重复元素了,而且求取的子集要去重

去重 可以只使用startIndex进行去重, 因为递归的时候下一个startIndex是i+1而不是0。

先进行排序 让相等的两个在一起 然后进行continue

如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        Arrays.sort(nums);
        backtracking(nums, 0, path, res);
        return res;
    }
    public void backtracking(int[] arr, int sIndex, List<Integer> path, List<List<Integer>> res){
        res.add(new ArrayList<>(path));
        if(sIndex >= arr.length) return;
        for(int i = sIndex; i < arr.length; i++){
            if(i > sIndex && arr[i - 1] == arr[i]) continue; 
            path.add(arr[i]);
            backtracking(arr, i + 1, path, res);
            path.remove(path.size() - 1);
        }
    }
}

posted @ 2024-11-26 18:19  小杭呀  阅读(13)  评论(0)    收藏  举报