Day23 力扣回溯 : 93.复原IP地址 | 78.子集 |90.子集II

Day23 力扣回溯 : 93.复原IP地址 | 78.子集 |90.子集II

93.复原IP地址

本期本来是很有难度的,不过 大家做完 分割回文串 之后,本题就容易很多了

题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html
视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/

第一印象:

我有个大概的思路,但是不会把String转成int,看题解在学习一下吧

看完题解的思路:

思路确实不难,难在终止条件? 如何切割? 怎么判断合法性?其实我觉得这道题好多剪枝的方法。

返回值和传参:

返回值void,传入字符串s,startIndex,另外还需要记录逗点 ‘.’ 的数量。因为这IP地址一共就是三个逗点,所以这个树深度也是确定的。

终止条件:

只要加了三次逗点,就可以return去收获结果了。

但是这里要注意,三个逗点分成了四个子串,每次都是检验逗点前面的字串是否合法。所以在收获结果的时候,还要检验最后的子串是否合法。

单层递归的逻辑:

和分割回文串那道题就是很像了,只是有一些细节上的改变。

例如,因为插入了逗点,所以下一次递归的位置就是 i + 2.

实现上的困难:

判断合法的函数我写的不对

在for循环里,如果合法,才会去递归,不合法就break,这个地方感觉怪怪的。

但是仔细想确实是,因为每个节点相当于一个for循环,只有合法的才回去递归一个,要是不合法就break,不要这个节点才对。

他这里操作字符串用的字符串直接加减,我觉得不太合理。

答案2用的stringBuilder。我需要学习一下这个东西好在哪。和字符串的一些操作

答案三有点超出能力了,二刷再说

感悟:

字符串操作太难了

在131.分割回文串中列举的分割字符串的难点,本题都覆盖了。

而且本题还需要操作字符串添加逗号作为分隔符,并验证区间的合法性。

可以说是131.分割回文串的加强版。

代码:

class Solution {
    List<String> result = new ArrayList<>();

    public List<String> restoreIpAddresses(String s) {
        if (s.length() > 12) return result; // 算是剪枝了
        backtracking(s, 0, 0);
        return result;
    }

    private void backtracking(String s, int startIndex, int pointSum) {
        //终止条件
        if (pointSum == 3) {
            if (isIP(s, startIndex, s.length() - 1)) {
                result.add(s);
            }
            return;
        }
        //单层递归
        for (int i = startIndex; i < s.length(); i++) {
            //如果这段是合法的
            if (isIP(s, startIndex, i)) {
                s = s.substring(0, i + 1) + '.' + s.substring(i + 1);
                pointSum++;
                backtracking(s, i + 2, pointSum);
                pointSum--;
                // 回溯删掉逗点
                s = s.substring(0, i + 1) + s.substring(i + 2);
            } else {
                break;
            }
        }

    }

    // //左闭右闭
    // private boolean isIP(String s, int startIndex, int end) {
    //     if (end > startIndex) return false;
    //     //获得字串
    //     String number = s.substring(startIndex, end + 1);
    //     //如果数字开头是0,但不只是0也不合法
    //     if (number.charAt(0) == '0' && number.length() != 1) return false;
    //     //如果有非数字不合法
    //     for (int i = 0; i < number.length(); i++) {
    //         if (number.charAt(i) > '9' || number.charAt(i) < '0') {
    //             return false;
    //         }
    //     }
    //     //转换成数字
    //     int num = Integer.parseInt(number);
    //     //不在范围内不合法
    //     if (num > 255 || num < 0) return false;
    //     return true;

    // }
    private Boolean isIP(String s, int start, int end) {
        if (start > end) {
            return false;
        }
        if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法
            return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 遇到⾮数字字符不合法
                return false;
            }
            num = num * 10 + (s.charAt(i) - '0');
            
        }
        if (num > 255) { // 如果⼤于255了不合法
                return false;
        }
        return true;
    }
}

78.子集

子集问题,就是收集树形结构中,每一个节点的结果。 整体代码其实和 回溯模板都是差不多的。

题目链接/文章讲解:https://programmercarl.com/0078.%E5%AD%90%E9%9B%86.html
视频讲解:https://www.bilibili.com/video/BV1U84y1q7Ci、

第一印象:

这道题和组合问题,有区别吗? 我做做试试

直接秒杀!!

看完题解的思路:

之前的组合问题收集最后的结果,而子集问题是收集每个节点的结果

组合问题可能需要剪枝,有的节点不要。而子集问题就是要遍历整个树。

这道题其实也可以不用终止条件,但我还是写了。因为到for循环那,startIndex >= nums.size(),本层for循环本来也结束了。

实现时的困难:

感悟:

学挺好

代码:

class Solution {
    LinkedList path = new LinkedList<>();
    List<List<Integer>> result = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        //添加空集
        result.add(new ArrayList<>(path));
        backtracking(nums, 0);
        return result;
    }

    private void backtracking(int[] nums, int startIndex) {
        //终止条件
        if (startIndex == nums.length) return;

        //单层递归逻辑
        for (int i = startIndex; i < nums.length; i++) {
            path.add(nums[i]);
            result.add(new ArrayList<>(path));
            backtracking(nums, i + 1);
            path.removeLast();
        }
    }
}

90.子集II

大家之前做了 40.组合总和II 和 78.子集 ,本题就是这两道题目的结合,建议自己独立做一做,本题涉及的知识,之前都讲过,没有新内容。

题目链接/文章讲解:https://programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html
视频讲解:https://www.bilibili.com/video/BV1vm4y1F71J

第一印象:

和子集那题相比,就是有重复的元素,也就是对于一些结果要去重,不能整个树都收集了。

我试试,和组合那里去重的题目一模一样,我用index来去重,不想用额外的数组标注是否使用过。

秒杀!忘记排序了提交错一次。

看完题解的思路:

ok!

实现中的困难:

忘记排序了

感悟:

子集问题和组合问题只是收集结果的时间不一样

代码:

class Solution {
    LinkedList<Integer> path = new LinkedList<>();
    List<List<Integer>> result = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        result.add(new ArrayList<>(path));
        Arrays.sort(nums);
        backtracking(nums, 0);
        return result;
    }

    private void backtracking(int[] nums, int startIndex) {
        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]);
            result.add(new ArrayList<>(path));
            backtracking(nums, i + 1);
            path.removeLast();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值