代码随想录算法训练营第21天 | 第八章 回溯算法 part03

第七章 回溯算法 part03

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/
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。例如:“0.1.2.201” 和 “192.168.1.1” 是 有效的 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效的 IP 地址。
这是判断条件,判断子串是否合法
段位以0为开头的数字不合法
段位里有非正整数字符不合法
段位如果大于255了不合法
又学到了一点
但是,实际在测试的时候,时间超出限制,正则表达式的复杂度较高,如果要提高效率,可以使用简单的范围检查代替正则表达式。

bool isValidNumber(const std::string& str) {
    // 正则表达式,匹配 0 到 255 且没有 0 开头的数字(除非是单独的 0)
    std::regex pattern("^(0|[1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5])$");
    // 使用std::regex_match来匹配字符串
    return std::regex_match(str, pattern);
}

^(0|[1-9][0-9]?|1[0-9]{2}|2[0-4][0-9]|25[0-5])$
正则表达式解释:
^ 和 $:分别匹配字符串的开头和结尾,确保整个字符串完全匹配。
0:匹配单独的数字0。
[1-9][0-9]?:匹配1到99之间的数字。[1-9]确保数字不以0开头,而[0-9]?表示后面可以跟一个数字(0到9)。
1[0-9]{2}:匹配100到199之间的数字。1开头,后面跟两个数字([0-9]{2})。
2[0-4][0-9]:匹配200到249之间的数字。2开头,第二个数字是0到4,第三个数字是0到9。
25[0-5]:匹配250到255之间的数字。
不得不说C++的库确实好强大

class Solution {
public:
vector<string> result;
bool isValid(string s, int startIndex,int i)
{
    string str = s.substr(startIndex, i - startIndex + 1);
        if (str.empty() || str.size() > 3) return false;  
        if (str[0] == '0' && str.size() > 1) return false;  
        int num = stoi(str);
        return num >= 0 && num <= 255;
}
void backtracking(string& s, int startIndex, int pointNum) {
     if (pointNum == 3) { 
    if (isValid(s, startIndex, s.size() - 1)) {
        result.push_back(s);
    }
    return;
    }
    for (int i = startIndex; i < s.size(); i++)
    if (isValid(s, startIndex, i)) { 
        s.insert(s.begin() + i + 1 , '.');  
        pointNum++;
        backtracking(s, i + 2, pointNum);   
        pointNum--;                        
        s.erase(s.begin() + i + 1);       
    } 
    else break; 
}
    vector<string> restoreIpAddresses(string s) {
        result.clear();
        if (s.size() < 4 || s.size() > 12) return result; 
        backtracking(s, 0, 0);
        return result;
    }
};

这个算法的核心是通过递归和回溯,在每个合法的位置插入点号,从而生成所有可能的 IP 地址组合。
isValid 函数负责判断当前部分的子串是否合法,而 backtracking 函数则通过插入点号、递归处理和回溯来逐步构造所有的可能解。
我发现回溯算法的代码都挺简单的,思路挺简单,但是写并不好写,一看就会,一做就废,还得多学多练。


78. 子集

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

题目链接/文章讲解https://programmercarl.com/0078.%E5%AD%90%E9%9B%86.html
视频讲解https://www.bilibili.com/video/BV1U84y1q7Ci
这题和之前的子集题目几乎一模一样,我都思考了半天去重的问题,结果发现没有重复的数据,那不是易如反掌。有手就行。简单到不能再简单了。不解释。

class Solution {
public:
vector<vector<int>> result; 
vector<int> path; 
vector<bool> used; 
void backtracking( vector<int>& nums ,int startIndex){
        result.push_back(path);
        if (path.size() <nums.size()) {    
            for(int i=startIndex;i<nums.size();i++)
            {
                path.push_back(nums[i]);
                backtracking(nums,i + 1); //继续递归
                path.pop_back();//回溯  
            }
        }
}
    vector<vector<int>> subsets(vector<int>& nums) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写 
        backtracking(nums, 0);
        return result;
    }
};

90. 子集II

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

题目链接/文章讲解https://programmercarl.com/0090.%E5%AD%90%E9%9B%86II.html
视频讲解:[https://www.bilibili.com/video/BV1vm4y1F71J]
本题本质上和去重子集完全一样

class Solution {
public:
vector<vector<int>> result; 
vector<int> path; 
vector<bool> used; 
void backtracking( vector<int>& nums ,int startIndex){
        result.push_back(path);
                  
            for(int i=startIndex;i<nums.size();i++)
            {
                if (i > startIndex && nums[i] == nums[i - 1]) {
                continue;  
            }        
                path.push_back(nums[i]);
                backtracking(nums,i + 1); //继续递归
                path.pop_back();//回溯  
            }
            //满足条件,return        
}
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写      
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return result;
    }
};

(https://www.bilibili.com/video/BV1vm4y1F71J)
本题也可以不使用used数组来去重,因为递归的时候下一个startIndex是i+1而不是0。

如果要是全排列的话,每次要从0开始遍历,为了跳过已入栈的元素,需要使用used。
今天的还是比较简单的,跟上次的子集合差不多。

  • 39
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值