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

LeetCode93

2025-02-21 17:00:41 星期五

题目描述:力扣93
文档讲解:代码随想录(programmercarl)93.复原IP地址
视频讲解:《代码随想录》算法视频公开课:回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址

代码随想录视频内容简记

要点1

关于如何在结果集添加最后的IP地址,这个我一开始写的还是一个result和一个path,但是发现这里就出问题了。定义的path是string类型的,但是string没法push_back的

string str = s.substr(index, i - index + 1);
path.push_back(str);

后面看了k哥的视频,这个不需要定义path,在终止的时候直接添加的是s,因为会在每次递归之前就把字符串修改好,关键还是要根据力扣给出的输出实例具体分析

要点2

本题的IP地址有效必须满足一个条件,那就是一共四个数,三个句点。这里要把句点数量当作参数传进去。

本题的第一感觉就是细节很多,自己先试着做了一下,不好想的

梳理

  1. 确定函数的参数和返回值

  2. 确定递归的终止条件

  3. 确定单层搜索的逻辑

大致代码内容

  1. void backtracking (string s, int index, int pointSum)这里需要用到pointSum控制句点的数量

  2. 终止条件就是pointSum == 3,另外因为一旦添加了第三个句点后,进入递归,直接判断最后的字符串是否有效即可push_back。因为四个整数的条件限制不能再进入递归了就

  3. 确定单层搜索的逻辑,这里就是需要在for循环中每次进入递归之前判断字符串是否有效。
    判断有效有三个点:

    1. 首位不能为0,否则false

    2. 字符串的值不能大于255,否则false

    3. 不能有非整数字符,否则false

LeetCode测试

这个题好恶心😭😭,折腾了半天就是在isValid函数上面

坑1——整数溢出

刚开始写的这样的,发现总会报整数溢出

for (int i = start; i <= end; i++) {
	if (s[i] > '9' || s[i] < '0') return false;
	// 这里针对大于255做判断
	num = num * 10 + (s[i] - '0');
}
if (num > 255) return false;

看了一下,举个例子把,刚开始分割的时候,比如25525511135这个字符串,第一次切到了这里,2|5525511135,之后的[index,i]区间内,他会逐个遍历,递归,回溯,直到[index, s.size() - 1],这时就会溢出

所以,避免这种可以在他*10之前进行判断,也就是放到里面,我看k哥是这么写的

for (int i = start; i <= end; i++) {
	if (s[i] > '9' || s[i] < '0') return false;
	// 这里针对大于255做判断
	num = num * 10 + (s[i] - '0');
	if (num > 255) return false;
}

坑2——关于首位是0的判断

一开始写的,if (end - start > 1 && s[start] == '0') return false;,一直没有看出来哪里有错,打印了一下


cout << "end:" << end << endl;
cout << "start:" << start << endl;
cout << s.substr(start, end - start + 1) << endl;
cout << s[start] << endl;

可以看到,end和start相差为1的时候,其实是有两位的,我没注意到。所以应该是end - start >= 1

坑3——关于start>end的判断

其实感觉这个判断就很奇怪,正常来讲是不应该出现这种情况的,怎么会start比end大呢?而且一不加上这个,最后的结果就会出错,太奇怪了。这个问题也琢磨了很久

然后我把异常打印了出一下

可以看到的是,异常是出现在index = 9,i = 8,一开始感觉很震惊,就把s字符串也顺便打印出来才明白的。出现异常的上一个index = 7, i = 7,其字符串是10.102.3,此时的index = 7正好指向末尾的3,在for循环中i初始为7,接着进入有效性判断中,isValid(s, 7, 7),一位数字3,没有问题,有效。那么接着就会插入一个句点,'.'变成10.102.3.。进入递归,注意,现在的i = 7。backtracking(s, 9, 3)

for (int i = index; i < s.size(); i++) {
	if (isValid(s, index, i)) {
		s.insert(s.begin() + i + 1, '.');
		pointSum++;
	} else continue;
	backtracking(s, i + 2, pointSum);
	s.erase(s.begin() + i + 1);
	pointSum--;
}

此时又会进入有效性判断,就会出现这个isValid(s, 9, 8),也就是这里之所以会出现这种情况,是代码本身的+2决定的,8也只是s.size() - 1后的结果,并不是i的值,感觉这里需要注意。

if (pointSum == 3) {
	if (isValid(s, index, s.size() - 1)) {
		result.push_back(s);
		return;
	}
}

之后,递归的结束,erase操作,会把第8位删除掉,也就是刚刚添加的'.',之后pointSum--恢复为8,for循环结束,之后,更上一层的递归结束,开始回溯......

也就是说,这里的if (start > end) return false是有特定意义的,代码的逻辑不变,这里就必须得加上

完整代码如下

这个题写的我整个人都不好了😵

点击查看代码
class Solution {
private:
    bool isValid(string s, int start, int end) {
        cout << "end:" << end << endl;
        cout << "start:" << start << endl;
        cout << "substr" << s.substr(start, end - start + 1) << endl;
        cout << "s[start]" << s[start] << endl;
        cout << s << endl;
        cout << endl;
        
        if (start > end) return false;
        if (end - start >= 1 && s[start] == '0') return false;
        
        
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s[i] > '9' || s[i] < '0') return false;
            // 这里针对大于255做判断
            num = num * 10 + (s[i] - '0');
            if (num > 255) return false;
        }
        
        
        return true;
    }

    vector<string> result;
    string path;
    void backtracking (string s, int index, int pointSum) {
        if (pointSum == 3) {
            if (isValid(s, index, s.size() - 1)) {
                result.push_back(s);
                return;
            }
        }
        
        for (int i = index; i < s.size(); i++) {
            if (isValid(s, index, i)) {
                s.insert(s.begin() + i + 1, '.');
                pointSum++;
            } else continue;
            backtracking(s, i + 2, pointSum);
            s.erase(s.begin() + i + 1);
            pointSum--;
        }
    }
public:
    vector<string> restoreIpAddresses(string s) {
        backtracking(s, 0, 0);
        return result;
    }
};

LeetCode78

题目描述:力扣78
文档讲解:代码随想录(programmercarl)78.子集
视频讲解:《代码随想录》算法视频公开课:回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集

代码随想录视频内容简记

需要注意的是,子集问题的结果集是存在于每一个结点的,包括根节点。我们在存放结果的时候,和之前的处理方式有不同,if (index == nums.size())表示的index不断取到直到等于集合的数量大小,但是这么做只能取到叶子结点。所以,push操作要和if判断分开

梳理

  1. 确定函数的参数和返回值

  2. 确定递归的终止条件

  3. 确定单层递归的逻辑

大致代码内容

  1. result.push_back(path)if (index == nums.size()) return,之所以push在if的前面,是因为要在最后index = 2指向3,加1之后变为3进入递归后,能把这次的子集添加进去,再返回

  2. 但层递归的逻辑和之前一样

大致代码内容

点击查看代码
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, int index) {
        result.push_back(path);
        if (index == nums.size()) return;
        for (int i = index; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsets(vector<int>& nums) {
        backtracking(nums, 0);
        return result;
    }
};

LeetCode90

题目描述:力扣90
文档讲解:代码随想录(programmercarl)90.子集II
视频讲解:《代码随想录》算法视频公开课:回溯算法解决子集问题,如何去重?| LeetCode:90.子集II

要点

  1. 核心的还是去重,对used数组的使用,注意这个if条件别写错了,一开始把nums[i] == nums[i - 1]写成used[i] != used[i - 1]了,不要写错了if (i >= 1 && nums[i] == nums[i - 1] && used[i - 1] == false)

  2. 注意对数组进行排序,用sort即可

LeetCode测试

点击查看代码
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int index, vector<bool>& used) {
        result.push_back(path);
        if (index == nums.size()) return;
        for (int i = index; i < nums.size(); i++) {
            if (i >= 1 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            path.push_back(nums[i]);
            used[i] = true;
            backtracking(nums, i + 1, used);
            path.pop_back();
            used[i] = false;
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<bool> used(nums.size(), false);
        backtracking(nums, 0, used);
        return result;
    }
};
posted on   bnbncch  阅读(916)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示