Day28(回溯)|93. 复原 IP 地址 78. 子集 90. 子集 II

93. 复原 IP 地址

  • 题目链接93. 复原 IP 地址
  • 解题思路:复原ip地址本质上也是切割问题,每次切割一部分加点,可以参考之前回文串分割的题。因为是ip分割,所以有一些约束,例如只能分割成四份,每份的值在0-255之间,除了0之外数字不能以0做前缀等。也可以抽象为树结构,每次切割一部分并回溯,需要维护两个用来标记位置的变量,一个startindex用于标记每次切割时的起始位置,还需要一个变量pointnum记录添加的分割点的数量,因为有四段的限制,pointnum为3时就应该直接讲剩余部分作为整体判断是否符合约束。
  • 主要难点:约束条件的添加,最后一段判断后的返回逻辑
  • 解题时间:30+5
  • 代码
class Solution {
    List<String> res=new ArrayList<>();//存放最终结果的数组
    public List<String> restoreIpAddresses(String s) {
        if(s.length()>12){
            return res;//总长度大于12肯定无法切割成ip形式
        }
        back(s,0,0);
        return res;
    }
    //s:原字符串
    //startIndex:每次切割时的起始位置记录
    //pointNum:已经添加的分隔符数量
    private void back(String s,int startIndex,int pointNum){
        if(pointNum==3){
            //分隔符数量为3则分割结束,将剩下的所有部分作为整体判断
            if(isVaild(s,startIndex,s.length()-1)){
                res.add(s);
            }
            return;
        }
        for(int i=startIndex;i<s.length();i++){
            if(isVaild(s,startIndex,i)){
                s=s.substring(0,i+1)+"."+s.substring(i+1);//插入分隔符
                pointNum++;//分隔符数量加1
                back(s,i+2,pointNum);//回溯,下一个起始位置为i+2
                pointNum--;
                s=s.substring(0,i+1)+s.substring(i+2);//删掉分隔符
            }else{
                //不符合要求,继续for循环
                break;
            }
        }
    }
    //用于判断分段部分是否满足ip地址要求的函数
    private boolean isVaild(String s,int start,int end){
        if(start>end){
            //长度为负?
            return false;
        }
        if(s.charAt(start)=='0'&&start!=end){
            //以0开头的非0部分不符合要求
            return false;
        }
        int num=0;
        for(int i=start;i<=end;i++){
            if(s.charAt(i)>'9'||s.charAt(i)<'0'){
                //数字不是0-9不合法
                return false;
            }
            //计算该段总和
            num=num*10+(s.charAt(i)-'0');
            if(num>255){
                //段值大于255不合法
                return false;
            }
        }
        return true;
    }
    
}

78. 子集

  • 题目链接78. 子集
  • 解题思路:本题和前面几个题都是建立树结构,不同的是之前的题是收集叶子节点,本体需要收集所有节点。在遍历时从原数组中抽取加入当前结点的序列,当原数组为空时说明到了叶子节点,则进行回溯。本题可以不用剪枝,因为本来就是需要获取所有的树节点,是一个全排列问题。
  • 解题时间:25+5
  • 代码
class Solution {
    List<List<Integer>> res = new ArrayList<>();// 存放最后结果集合
    LinkedList<Integer> path = new LinkedList<>();// 存放每次结果
    public List<List<Integer>> subsets(int[] nums) {
        back(nums,0);
        return res;
    }
    private void back(int[] nums,int startIndex){
        res.add(new ArrayList<>(path));
        if(startIndex>=nums.length){
            return;
        }
        for (int i = startIndex; i < nums.length; i++){
            path.add(nums[i]);
            back(nums, i + 1);
            path.removeLast();
        }
    }
}

90. 子集 II

  • 题目链接90. 子集 II
  • 解题思路:和上题类似,区别是集合里有重复元素了,并且要求返回集合不能有重复的结果,所以需要加入去重的步骤。在树结构中,从上到下一条路径上是可以加入重复元素的,但是在同一层中进行选取时不能选同样元素,可以通过维护一个used数组来标记哪些元素已经在当前层被加入。
  • 主要难点:树层去重(二刷重点看)
  • 解题时间:25+5
  • 代码
class Solution {
   List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
   LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果
   boolean[] used;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums.length == 0){
            result.add(path);
            return result;
        }
        Arrays.sort(nums);
        used = new boolean[nums.length];
        subsetsWithDupHelper(nums, 0);
        return result;
    }
    
    private void subsetsWithDupHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length){
            return;
        }
        for (int i = startIndex; i < nums.length; i++){
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]){
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            subsetsWithDupHelper(nums, i + 1);
            path.removeLast();
            used[i] = false;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值