算法训练营Day25

#Java #回溯

开源学习资料

Feeling and experiences:

复原IP地址:力扣题目链接

有效 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 中的任何数字。你可以按 任何 顺序返回答案。

之前做过分割回文字符串,其中的关键点就在于对回文的判断,还有分割线的设定

该题目也很类似:

java 复制代码
class Solution {
    //还是设置两个全局变量
    List<String> ans = new ArrayList<>();
    List<String> path = new ArrayList<>();
    public List<String> restoreIpAddresses(String s) {
    //思路和分割回文串相似
     if(s.length() == 0){
         return new ArrayList<>();
     }
     back(s,0);
     return ans;
    }

    public void back(String s,int startIndex){
        //终止条件
        
        if(path.size() > 4){
            return;
        }
        if(path.size() == 4 && startIndex >= s.length()){
            String temp = String.join(".",path);
            ans.add(temp);
            return;
        }

        for(int i = startIndex;i<s.length();i++){
            if(isValid(s,startIndex,i)){
                String str = s.substring(startIndex,i+1);
                path.add(str);
            }
            else{
                continue;
            }
            back(s,i+1);
            path.remove(path.size()-1);
        }
 
    }
    //判断字符串在区间[start,end]是否合法
    public boolean isValid(String s , int start,int end){
        if(start > end){
            return false;
        }
        //不能有前导0
        if(s.charAt(start) == '0' && start != end){
            return false;
        }
        //不能有非数字
        int num = 0;
        for(int i = start; i<=end;i++){
            if(s.charAt(i) > '9' || s.charAt(i) < '0'){
                return false;
            }
            //不能大于255
            num = num * 10 + (s.charAt(i) - '0');
            if(num > 255){
                return false;
            }
        }
        return true;
    }
}

与分割回文串的差别:

终止条件:

• 如果path中的段数超过4,说明不是有效的IP地址,直接返回。

• 如果path中的段数等于4且遍历完了整个字符串s,则将path中的段用点号连接起来,加入到ans列表中。

• 从startIndex开始,遍历字符串s的每个字符。

• 调用isValid函数判断当前切割的子串是否有效。如果有效,将其加入path中。

• 递归调用back函数,探索下一个字符。

• 回溯:完成对当前段的探索后,从path中移除最后加入的段。

还有就是字符串的合法条件(不难,但是多~有些地方容易漏掉)

子集:力扣题目链接

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

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

都是在回溯这一章节,那这道题又和之前的题有什么区别呢?

我感觉代码随想录中有一句话总结的很好:

子集是收集树形结构中树的所有节点的结果

而组合问题、分割问题是收集树形结构中叶子节点的结果

java 复制代码
class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
    
    back(nums,0);
    return ans;
    }

    public void back(int[] nums,int startIndex){
       //终止条件
       ans.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.remove(path.size()-1);
       }
    }
}

这道题练习完,可以把它归为一道模板题

注意:终止条件中的if判断可以删除,(本来就是一个栈,最终会弹栈)

子集II:力扣题目链接

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

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

这道题在子集的基础又有运用了之前 组合II 问题的去重(可以说是,子集模板 + 去重模板)

java 复制代码
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;
        }
    }
}

同样的,可以不用used数组:

java 复制代码
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums.length == 0){
            result.add(path);
            return result;
        }
        Arrays.sort(nums);
        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 > startIndex && nums[i] == nums[i - 1]){
                continue;
            }
            path.add(nums[i]);
            subsetsWithDupHelper(nums, i + 1);
            path.removeLast();
        }
    }
}

江畔何人初见月?

江月何年初照人?

Fighting!

相关推荐
.YM.Z8 分钟前
【数据结构】:排序(一)
数据结构·算法·排序算法
Chat_zhanggong34511 分钟前
K4A8G165WC-BITD产品推荐
人工智能·嵌入式硬件·算法
百***480726 分钟前
【Golang】slice切片
开发语言·算法·golang
q***925133 分钟前
Windows上安装Go并配置环境变量(图文步骤)
开发语言·windows·golang
墨染点香39 分钟前
LeetCode 刷题【172. 阶乘后的零】
算法·leetcode·职场和发展
做怪小疯子41 分钟前
LeetCode 热题 100——链表——反转链表
算法·leetcode·链表
凭栏落花侧2 小时前
打印机脱机状态应该怎么处理
windows
做怪小疯子3 小时前
LeetCode 热题 100——矩阵——旋转图像
算法·leetcode·矩阵
努力学习的小廉3 小时前
我爱学算法之—— BFS之最短路径问题
算法·宽度优先
高山上有一只小老虎3 小时前
构造A+B
java·算法