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

93.复原IP地址

点击查看代码
class Solution {
public:
    vector<string> result;
    //检查切割下来的区间[startIndex, endIndex]是否为合法IP字段
    bool isValidIP(string &s, int startIndex, int endIndex) {
        //注意勿漏条件:endIndex < startIndex 否则会将x.y.z.这样的IP加入结果集
        if(endIndex < startIndex || endIndex - startIndex + 1 > 4) return false;
        if(s[startIndex] == '0' && endIndex - startIndex + 1 > 1) return false;
        int sum = 0;
        for(int i = startIndex; i <= endIndex; ++i) {
            sum *= 10;
            sum += s[i] - '0';
            if(sum > 255) return false;
        }
        return true;
    }
    void backtracking(string &s, int startIndex, int pointNum) {
        //合法才会加pointNum并进入递归,当pointNum == 3时,说明前三段合法,需要再额外检查第四段是否合法
        if(pointNum == 3) {
            if(isValidIP(s, startIndex, s.size() - 1)) result.push_back(s);
            return;
        }

        for(int i = startIndex; i < s.size(); ++i) {
            if(isValidIP(s, startIndex, i)) {
                s.insert(i + 1, ".");  //注意:即使只插入一个字符,第二个参数也需要写成字符串形式
            }
            else break; //出现非法后,本层循环直接结束,因为再往后切也必然非法
            backtracking(s, i + 2, pointNum + 1); //注意加了'.',故下一层循环从i + 2开始切割
            s.erase(i + 1, 1);
        }
    }

    vector<string> restoreIpAddresses(string s) {
        backtracking(s, 0, 0);
        return result;
    }
};

78.子集

点击查看代码
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;

    void backtracking(vector<int>& nums, int startIndex) {
        //子集问题需要收集树中所有节点,即每选取一个元素
        //收集必须在递归终止判断之前,否则当收集的是最后一个元素时,传进来的startIndex
        //必然等于nums.size(),导致还没收集就return了
        result.push_back(path); 
        if(startIndex == nums.size()) return;

        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) {
        backtracking(nums, 0);
        return result;
    }
};

组合和分割是收集叶子节点,而子集问题需要收集树上所有节点(即每选完一个数字就收集)

90.子集II
与上题相比增加了对原始nums数组进行排序和去重逻辑

点击查看代码
class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;

    void backtracking(vector<int>& nums, int startIndex) {
        //子集问题需要收集树中所有节点,即每选取一个元素
        //收集必须在递归终止判断之前,否则当收集的是最后一个元素时,传进来的startIndex
        //必然等于nums.size(),导致还没收集就return了
        result.push_back(path); 
        if(startIndex == nums.size()) return;

        for(int i = startIndex; i < nums.size(); ++i) {
            //去重逻辑:每层循环从第二个元素开始去重
            //同一个层for循环进行去重即树层去重,树枝不去重
            //对比78.子集只多了对原始nums进行排序和下面这句去重逻辑
            if(i > startIndex && nums[i] == nums[i - 1]) continue;
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        //必须必须必须排序才能去重
        sort(nums.begin(), nums.end());
        backtracking(nums, 0);
        return result;
    }
};

2025/03/06

posted @ 2025-03-06 22:12  coder小杰  阅读(11)  评论(0)    收藏  举报