目录
Leetcode 93.复原IP地址
题目链接:Leetcode 93.复原IP地址
题目描述:给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。
有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效的 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效的 IP 地址。
思路:这道题也是一道典型分割类型题目,可以用回溯算法来做。每层搜索的时候判断分割的字符串是否符合题意,然后在字符串中加上“ . ”。
代码如下:
class Solution {
public:
vector<string> result;
bool isValid(const string& s, int start, int end) { //判断闭区间[start,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;
}
void backtracking(string& s, int start, int pointNum) {
if (pointNum == 3) {
if (isValid(s, start, s.size() - 1)) {
result.push_back(s);
}
return;
}
for (int i = start; i < s.size(); i++) {
if (isValid(s, start, i)) {
s.insert(s.begin() + i + 1, '.'); //在i的后面加上"."
pointNum++;
backtracking(s, i + 2, pointNum); //下一次寻找起始位置为i+2
pointNum--;
s.erase(s.begin() + i + 1);
} else
break; //不合法直接结束本层循环
}
}
vector<string> restoreIpAddresses(string s) {
if (s.size() < 4 || s.size() > 12)
return result;
backtracking(s, 0, 0);
return result;
}
};
- 时间复杂度: O(3^4),IP地址最多包含4个数字,每个数字最多有3种可能的分割方式,则搜索树的最大深度为4,每个节点最多有3个子节点。
- 空间复杂度: O(n)
Leetcode 78.子集
题目链接:Leetcode 78.子集
题目描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
思路:
方法一:这道题属于子集问题,也可以用回溯来解决。对于回溯法需要注意的点就是收集子集需要在终止条件之前,否则会把本层结果漏掉。
代码如下:(回溯法)
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int start) {
result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
for (int i = start; 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;
}
};
- 时间复杂度: O(n * 2^n)
- 空间复杂度: O(n)
不过这道题比较简单,即使不用回溯也可以解决。
方法二:对于一个集合的某个元素,我们求子集的过程中有两种选择:选or不选,因此对所有元素,枚举每个元素选与不选的情况进行组合,就可以得到所有子集。由于二进制是由0和1组成,因此可以借助1和0表示选和不选,通过嵌套循环枚举,并判断某一个数字是否为1。
代码如下:(二进制和位运算)
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
for (int i = 0; i < (1 << nums.size()); i++) {
vector<int> path;
for (int j = 0; j < nums.size(); j++) {
if (((i >> j) & 1) == 1) {
path.push_back(nums[j]);
}
}
result.push_back(path);
}
return result;
}
};
Leetcode 90.子集II
题目链接:Leetcode 90.子集II
题目描述:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
思路:这道题也属于子集问题,只不过需要去重,仍然是回溯法。需要注意的是,去重需要先将集合排序。
代码如下:
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int> nums, int start) {
result.push_back(path);
for (int i = start; i < nums.size(); i++) {
//注意,这里要对同一层使用过的元素进行跳过
if (i > start && 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;
}
};
总结:回溯法思路简单,但是代码很难一次写对。不过第一道题还是有些难度。
最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!