代码随想录Day25-回溯:力扣第93m、78m、90m、491m题

93m. 复原IP地址

题目链接
代码随想录文章讲解链接

方法一:回溯搜索

用时:51m39s

思路

用n来记录当前已经确定了n组数字,当n等于4时,表示4组数字都确定了,若此时刚好也遍历完了全部字符,则将当前结果记录。

  • 时间复杂度: O ( n ) O(n) O(n),每层递归有3种情况,最多递归4层,将结果拷贝到答案数组中需要 O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
private:
    vector<string> res;
    string path;

    void backTracking(string& s, int n, int begin) {
        // 当获取了4个数字,并且遍历完全部字符,将结果记录并终止递归
        if (n == 4 && begin == s.length()) res.push_back(path);

        // 剪枝:如果接下来每个数字都取一位数所需的长度也大于字符串总长度,则直接终止
        // 剪枝:如果接下来每个数字都取三位数所需的长度也小于字符串总长度,则直接终止
        if (n >= 4 || begin + 4 - n > s.length() || begin + (4 - n) * 3 < s.length()) return;

        if (n > 0) path.push_back('.');
        for (int i = 1; i <= 3 && i <= s.length() - begin; ++i) {
            // 剪枝:如果数字前导为0或者大于255,则终止递归
            if ((i > 1 && s[begin] == '0') || (i == 3 && (s[begin] - '0') * 100 + (s[begin + 1] - '0') * 10 + (s[begin + 2] - '0') > 255)) break;

            path.append(s.substr(begin, i));
            backTracking(s, n + 1, begin + i);  // 递归
            for (int j = i; j > 0; --j) path.pop_back();  // 回溯
        }
        if (n > 0) path.pop_back();  // 回溯
    }

public:
    vector<string> restoreIpAddresses(string s) {
        backTracking(s, 0, 0);
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

主要是代码实现时的一些细节问题。


78m. 子集

题目链接
代码随想录文章讲解链接

方法一:回溯搜索(自己想的)

用时:19m50s

思路

把问题拆分成从nums中无重复的选取amount个数字,遍历不同的amount时的情况,从nums中无重复的选取amount个数字可以用回溯搜索实现。

  • 时间复杂度: O ( n ⋅ 2 n ) O(n \cdot 2^n) O(n2n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
private:
    vector<vector<int>> res;
    vector<int> path;

    void backTracking(vector<int>& nums, int begin, int acount) {
        if (amount == 0) res.push_back(path);  // 当要拿取的数量等于0,则终止递归
        else {
            // 从当前起始位置前进i步后,拿取元素
            for (int i = 0; i <= nums.size() - begin - amount; ++i) {
                path.push_back(nums[begin + i]);  // 拿取元素
                // 起始位置更新为拿取位置+1,目前还要拿取的数量-1
                backTracking(nums, begin + i + 1, amount - 1);  // 递归
                path.pop_back();  // 回溯
            }
        }
    }

public:
    vector<vector<int>> subsets(vector<int>& nums) {
        // 从nums中选取acount个数字
        for (int amount = 0; amount <= nums.size(); ++amount) backTracking(nums, 0, amount);
        return res;
    }
};

方法二:回溯搜索

思路

在回溯搜索整棵树的过程中,每一步都将路径记录。

  • 时间复杂度: O ( n ⋅ 2 n ) O(n \cdot 2^n) O(n2n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
private:
    vector<vector<int>> res;
    vector<int> path;

    void backTracking(vector<int>& nums, int begin) {
        res.push_back(path);
        for (int i = begin; 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 res;
    }
};

方法三:位运算迭代

思路

nums中每一位是否选取可以用二进制掩码来表示,所以子集共有 2 n 2^n 2n个,遍历每个数,然后用位运算获取对应位置是否抓取,以此遍历得到子集。

  • 时间复杂度: O ( n ⋅ 2 n ) O(n \cdot 2^n) O(n2n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> tmp;
        vector<vector<int>> res;
        int size = nums.size();
        int tot = 1 << size;
        for (int mask = 0; mask < tot; ++mask) {
            tmp.clear();
            for (int i = 0; i < size; ++i) {
                if (1 << i & mask) tmp.push_back(nums[i]);
            }
            res.push_back(tmp);
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


90m. 子集II

题目链接
代码随想录文章讲解链接

方法一:回溯搜索

用时:7m48s

思路

在上一题的基础上去重。

  • 时间复杂度: O ( n ⋅ 2 n ) O(n \cdot 2^n) O(n2n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
private:
    vector<vector<int>> res;
    vector<int> path;

    void backTracking(vector<int>& nums, int begin) {
        res.push_back(path);
        for (int i = begin; i < nums.size(); ++i) {
            if (i > begin && nums[i] == nums[i - 1]) continue;  // 去重
            path.push_back(nums[i]);
            backTracking(nums, i + 1);
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backTracking(nums, 0);
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


491m. 递增子序列

题目链接
代码随想录文章讲解链接

方法一:回溯搜索(自己想的)

用时:16m37s

思路
  • 时间复杂度: O ( n ⋅ 2 n ) O(n \cdot 2^n) O(n2n)
  • 空间复杂度: O ( 2 n ) O(2^n) O(2n)
C++代码
class Solution {
private:
    vector<vector<int>> res;
    vector<int> path;

    void backTracking(vector<int>& nums, int begin) {
        if (path.size() > 1) res.push_back(path);  // 当序列的长度大于等于2时,保存结果
        unordered_set<int> hashSet;  // 记录已经遍历过的元素,用于去重
        for (int i = begin; i < nums.size(); ++i) {
            // 去重
            if (hashSet.find(nums[i]) != hashSet.end()) continue;
            hashSet.insert(nums[i]);
            // 保证单调增
            if (path.size() > 0 && nums[i] < path.back()) continue;
            // 递归
            path.push_back(nums[i]);
            backTracking(nums, i + 1);
            // 回溯
            path.pop_back();
        }
    }
    
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        backTracking(nums, 0);
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


最后的碎碎念

=_=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值