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(n⋅2n)。
- 空间复杂度: 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(n⋅2n)。
- 空间复杂度: 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(n⋅2n)。
- 空间复杂度: 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(n⋅2n)。
- 空间复杂度: 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(n⋅2n)。
- 空间复杂度: 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;
}
};
看完讲解的思考
无。
代码实现遇到的问题
无。
最后的碎碎念
=_=