[豪の算法奇妙冒险] 代码随想录算法训练营第二十四天 | 93-复原IP地址、78-子集、90-子集Ⅱ

代码随想录算法训练营第二十四天 | 93-复原IP地址、78-子集、90-子集Ⅱ


LeetCode93 复原IP地址

题目链接:https://leetcode.cn/problems/restore-ip-addresses/description/

文章讲解:https://programmercarl.com/0093.复原IP地址.html

视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 切割问题就可以使用回溯搜索法把所有可能性搜出来,抽象为树型结构

​ 本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。dotCnt表示逗点数量,dotCnt为3说明字符串已被分成4段了,然后验证一下第四段是否合法,如果合法就将该字符串加入到结果集里

​ 单层逻辑中,如果合法就在字符串后面加上符号 . 表示已经分割;如果不合法就结束本层循环,剪掉同层剩余分支

​ 判断分割的字符子串是否合法,主要考虑:以0为开头的数字不合法,有非正整数字符不合法、数字大于255了不合法、索引指针start大于end不合法

image-20260106160205934

class Solution {
    List<String> result = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
        StringBuilder str = new StringBuilder(s);
        backTracking(str, 0, 0);
        return result;
    }

    public boolean isValid(StringBuilder str, int start, int end){
        if(start > end){
            return false;
        }

        if(str.charAt(start) == '0' && start != end){
            return false;
        }

        int num = 0;
        for(int i = start; i <= end; i++){
            int digit = str.charAt(i) - '0';
            num = 10*num + digit;
            if(num > 255){
                return false;
            }
        }

        return true;
    }

    public void backTracking(StringBuilder str, int start, int dotCnt){
        if(dotCnt == 3 && isValid(str, start, str.length()-1)){
            result.add(str.toString());
            return;
        }
        for(int i = start; i < str.length(); i++){
            if(isValid(str, start, i)){
                str.insert(i+1, '.');
                backTracking(str, i+2, dotCnt+1);
                str.deleteCharAt(i+1);
            }else{
                break;
            }
        }
    }
}

LeetCode78 子集

题目链接:https://leetcode.cn/problems/subsets/description/

文章讲解:https://programmercarl.com/0078.子集.html

视频讲解:https://www.bilibili.com/video/BV1U84y1q7Ci/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 如果把子集问题、组合问题、分割问题都抽象为树形结构的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点

​ 子集也是一种组合问题,因为它是无序的,取过的元素不会重复取,写回溯算法的时候,for要从startIndex开始,而不是从0开始

image-20260106161609276

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> records = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backTracking(nums, 0);
        return result;
    }

    public void backTracking(int[] nums, int start){
        result.add(new ArrayList<>(records));
        for(int i = start; i < nums.length; i++){
            records.add(nums[i]);
            backTracking(nums, i+1);
            records.remove(records.size()-1);
        }
    }
}

LeetCode90 子集Ⅱ

题目链接:

文章讲解:https://programmercarl.com/0090.子集II.html

视频讲解:https://www.bilibili.com/video/BV1vm4y1F71J/?vd_source=b989f2b109eb3b17e8178154a7de7a51

​ 元素在同一个组合内是可以重复的,但两个组合不能相同,所以要树层去重,树枝不用去重

​ 树层去重的思路和 LeetCode40 组合总和Ⅱ 的很相似,如果 nums[i] == nums[i - 1] 并且 used[i - 1] == false,就说明前一个树枝使用了nums[i - 1],也就是说同一树层使用过nums[i - 1],此时for循环里就应该做continue的操作,做树层的去重

image-20260106162909927

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> records = new ArrayList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        boolean[] used = new boolean[nums.length];
        backTracking(nums, 0, used);
        return result;
    }

    public void backTracking(int[] nums, int start, boolean[] used){
        result.add(new ArrayList<>(records));
        for(int i = start; i < nums.length; i++){
            if(i > 0 && nums[i] == nums[i-1] && used[i-1] == false){
                continue;
            }
            records.add(nums[i]);
            used[i] = true;
            backTracking(nums, i+1, used);
            used[i] = false;
            records.remove(records.size()-1);
        }
    }
}
posted @ 2026-01-06 16:34  SchwarzShu  阅读(2)  评论(0)    收藏  举报