第七章 回溯算法part04 93. 复原IP地址 78. 子集 90. 子集II

第二十八天| 第七章 回溯算法part04 93. 复原IP地址 78. 子集 90. 子集II

一、93. 复原IP地址

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

  • 题目介绍:

    • 有效 IP 地址 正好由四个整数(每个整数位于 0255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

      • 例如:"0.1.2.201" "192.168.1.1"有效 IP 地址,但是 "0.011.255.245""192.168.1.312""192.168@1.1"无效 IP 地址。

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

    • 示例 1:

      输入:s = "25525511135"
      输出:["255.255.11.135","255.255.111.35"]
      
  • 思路:

    • 和切割回文一样,[startIndex, i]为一个子串,这里不同的是,需要在原字符串中插入一个"."
    • 插入的位置在i + 1处
    • 因此,下一次递归的时候应该从i + 2的位置开始
  • 代码:

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

    // 1. 确定返回值和参数
    //  1.1 这里参数选用的是StringBuilder,这样为的是方便插入操作
    public void backtracking(StringBuilder s, int startIndex, int pointSum) {
        // 2. 结束条件
        if (pointSum == 3) {
            if (isValid(s, startIndex, s.length() - 1)) {
                result.add(s.toString());
            }
            return;
        }
        // 3. 单层循环操作
        for (int i = startIndex; i < s.length(); i++) {
            if (isValid(s, startIndex, i)) {
                //  3.1 如果[startIndex, i]这个区间是满足IP条件的,就在i + 1的位置,插入一个.
                s.insert(i + 1, '.');
                //  3.2 递归
                //  注意:这里为什么是i + 2呢?因为本来是要找下一个元素,但是现在插入了一个点
                backtracking(s, i + 2, pointSum + 1);
                //  把刚刚插入的.删除掉
                s.deleteCharAt(i + 1);
            }
        }
        return;
    }

    public boolean isValid(StringBuilder s, int start, int end) {
        if(start > end)
            return false;
        // 不是一位数,并且第一位为0,false
        if(s.charAt(start) == '0' && start != end)
            return false;
        // 计算s的数值是否大于255
        int num = 0;
        for (int i = start; i <= end; i++) {
            // 遇到⾮数字字符不合法,false
            if (s.charAt(i) > '9' || s.charAt(i) < '0') {
                return false;
            }
            num = num * 10 + (s.charAt(i) - '0');
            if (num > 255) {
                return false;
            }
        }
        return true;
    }
}
  • 注意:
    • 递归结束的条件为:整个字符串中的“.”的个数如果等于三个,说明该结束了
    • 需要判断从当前的startIndex,到字符串的末尾是否合法
    • 如果合法就将整个字符串add到result中

二、78. 子集

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

  • 题目介绍:

    • 给你一个整数数组 nums数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

      解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

  • 思路:

    • (1)确定参数和返回值:String s, int startIndex
    • (2)递归结束的条件:
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    • (3)单层搜索的逻辑:每次要遍历一个集合的时候,把当前的path进行结果收集
      • 从图中红线部分,可以看出遍历这个树的时候,把所有节点都记录下来,就是要求的子集集合
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 代码:

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

    // 1.确定参数和返回值
    public void backtracking(int[] nums, int startIndex) {
        // 这里收集结果
        result.add(new ArrayList<>(path));
        // 2.递归结束的条件
        if (startIndex >= nums.length) {
            return;
        }
        // 3.单层搜索的逻辑
        for (int i = startIndex; i < nums.length; i++) {
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
        return;
    }
}
  • 与组合、切割问题的的区别:
    • 组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

三、90. 子集II

  • 题目链接:

  • 题目介绍:

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

      解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

  • 思路:

    • 与子集I题目不同的是,给定数组中的元素**可能存在重复的**
    • 但最后要求的解集是不能包含重复元素的,因此需要去重
    • 去重的逻辑和组合总和II、三数之和、四数之和一样
    • 只要是去重,就要先排序。这里依旧是把去重逻辑定位到一个集合中处理,即树层去重
  • 代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        backtracking(nums, 0);
        return result;
    }
    
    public 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);
        }
        return;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值