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

93.复原IP地址 ⭐️

本题还是分割问题

难的在于字符串的处理

  • 解题思路:
    ①要处理的字符串是不断修改的,难点在于修改的过程
    ②本题不同之处,终止条件改为加点个数,而不是和切割字符串一样完整的分割字符串
    是否有效处理:涉及到三个方面的检查

  • 图像理解

    2023-04-11T19_29_33

    2023-04-11T19_30_12

public class Solution {

    private List<String> res = new ArrayList<>();

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


    /**
     * 
     * @param s           处理的字符串  
     * @param startIndex  每一层的分割位置
     * @param pointSum 	  表示加点的个数
     */
    private void backtracking(String s, int startIndex, int pointSum) {

        if(pointSum == 3){
            //判断最后一个点到最后有效
            if(isStringValid(s, startIndex, s.length() - 1)){
                res.add(new String(s));
                return;
            }
        }

        //处理逻辑
        for (int i = startIndex; i < s.length(); i++) {
            
            
            //判断是否满足加入条件
            if(isStringValid(s, startIndex, i)){
                s = s.substring(0, i + 1) + "." + s.substring(i + 1);

                pointSum++;

                //i + 1的位置.已经占了
                backtracking(s, i + 2, pointSum);

                //回溯
                pointSum--;
                s = s.substring(0, i + 1) + s.substring(i + 2);
            }else{
                //进行剪枝操作
                break;
            }

        }


    }

    //[start,end]
    private boolean isStringValid(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){
                return false;
            }

        }

        return true;

    }

}

78.子集

题目链接:代码随想录

本题为搜集子集问题,与之前的组合问题不同,本题需要搜集树上的所有节点,因此也不需要剪枝
那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

  • 解题思路:

    ①参数:要传入要找子集的数组和防止重复选取的startIndex
    ②终止条件:就是startIndex已经大于数组的长度了,就终止了,因为没有元素可取了
    ③单层逻辑: 每一次加上nums[i],表示本层选取的元素,再深入下一层取元素
    ④收集元素:该树上的每个节点都要收集,因此每一层刚开始要收集本层元素

  • 图像理解

    2023-04-11T20_15_46

    2023-04-11T20_19_47

public class Solution1 {



    private List<List<Integer>> res = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {


        backtracking(nums,0);

        return res;


    }


    private void backtracking(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]);

            backtracking(nums, i + 1);//防止重复

            //回溯
            path.remove(path.size() - 1);

        }

    }
}

90.子集II

本题解法:子集问题 + 去重

题目链接:代码随想录

  • 解题思路:
    ①startIndex防止重复使用数组中某一元素
    ②used数组进行树枝去重

  • 图像理解:

    2023-04-11T21_01_04

public class Solution2 {

    private List<List<Integer>> res = new ArrayList<List<Integer>>();
    private List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        int[] used = new int[nums.length];

        //一定要先排序
        Arrays.sort(nums);
        backtracking(nums,0,used);

        return res;
        
    }

    private void backtracking(int[] nums,int startIndex,int[] used){

        //把每一层收集到的元素添加到结果集中
        res.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] == 0){
                continue;//跳过这一分支
            }

            used[i] = 1;//当前元素使用过了
            path.add(nums[i]);

            backtracking(nums, i + 1, used);//i + 1防止重复使用数组中某一元素

            //回溯
            used[i] = 0;
            path.remove(path.size() - 1);

        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值