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

目录

93.复原IP地址

前言

算法实现

78.子集

前言

算法实现

90.子集II

前言

算法实现

总结


93.复原IP地址

题目链接

文章链接

前言

         本题与上一题分割回文串类似,本质都是类似组合的切割问题,对有效的ip地址进行切分复原,切割问题就可以使用回溯搜索法把所有可能性搜出来。

算法实现

class Solution {
private:
    vector result;
    void backtracking(string& s, int startIndex, int pointNum){
        if (pointNum == 3){ // 验证第四段是否合法
            if (isVaild(s, startIndex, s.size() - 1)){
                result.push_back(s);
            }
            return;
        }
        for (int i = startIndex; i < s.size(); i++){
            if (isVaild(s, startIndex, i)){
                s.insert(s.begin() + i + 1, '.');
                pointNum++;
                backtracking(s, i + 2, pointNum);
                s.erase(s.begin() + i + 1);
                pointNum--;
            }
            else break;
        }
    }
    // 自定义判断ip地址合法函数
    bool isVaild(const string& s, int start, int end){
        if (start > end){
            return false;
        }
        if (s[start] == '0' && start != end){
            return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++){
            if (s[i] > '9' || s[i] < '0'){
                return false;
            }
            num = num * 10 + (s[i] - '0');
            if (num > 255){
                return false;
            }
        }
        return true;
    }
public:
    vector restoreIpAddresses(string s) {
        result.clear();
        if (s.size() < 4 || s.size() > 12) return result; // 提前进行合理判断,类似剪枝
        backtracking(s, 0, 0);
        return result;
    }
};

        本题与切割回文子串的区别主要在于递归终止条件以及在每次递归中要加入‘.’,本题明确要求只会分成4段,所以不能用切割线切到最后作为终止条件,而是分割的段数作为终止条件。

78.子集

题目链接

文章链接

前言

         子集问题的处理方式与前面的组合问题又不太一样,如果把子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!

        其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!

算法实现

class Solution {
private:
    vector> result;
    vector path;
    void backtracking(vector& nums, int startIndex){
        result.push_back(path);
        // 该处的if判断可以省略
        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();
        }
    }
public:
    vector> subsets(vector& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

90.子集II

题目链接

文章链接

前言

         本题在前一题子集的基础上增加了集合中有重复元素的条件,并且不能有重复的子集。本质上是组合总和II和上一题子集的综合,既考察了求集合子集的操作也考察了去重操作。

算法实现

class Solution {
private:
    vector> result;
    vector path;
    void backtracking(vector& nums, int startIndex, vector used){
        result.push_back(path);
        if (startIndex > nums.size()){
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < nums.size(); i++){
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0){
                continue;
            }
            path.push_back(nums[i]);
            used[i] = 1;
            backtracking(nums, i + 1, used);
            used[i] = 0;
            path.pop_back();
        }
    }
public:
    vector> subsetsWithDup(vector& nums) {
        result.clear();
        path.clear();
        vector used(nums.size(), 0);
        sort(nums.begin(), nums.end());
        backtracking(nums, 0, used);
        return result;
    }
};

          在上题的基础上增加了去重的操作,通过引入used数组进行树层去重。

总结

        今天接触了子集问题,处理方式上与组合问题类似,只是在收集的时候要对每层节点进行收集,而不是单单对叶子节点进行收集。

你可能感兴趣的:(算法)