93. 复原IP地址
整体还是使用了回溯模板,但是多了剪枝条件:
1. num表示段数,超过3就得直接返回,后面的情况不用考虑
2. 每段的数值在0-255之间,且二位数以上首位数字不能为‘0’
class Solution {
public:
string path;
vector<string> ans;
void backtrace(string s, int startID, int num) {
if (num == 4 && startID == s.size()) {
ans.push_back(path);
return;
}
// 如果超过 4 段或者字符未用完但已达到 4 段,则剪枝
if (num >= 4) return;
for (int i = startID; i < s.size(); ++i) {
string str = s.substr(startID, i - startID + 1);
if(str.size() > 3) return;
int Inum = stoi(str);
// 剪枝:不能超过 255,不能有前导 0
if ((str.size() > 1 && str[0] == '0') || Inum > 255) continue;
// 记录当前 path
int pathSize = path.size();
path += str;
if (num < 3) {
path.push_back('.');
}
backtrace(s, i + 1, num + 1);
// 恢复现场
path.erase(pathSize);
}
}
vector<string> restoreIpAddresses(string s) {
backtrace(s,0,0);
return ans;
}
};
78. 子集
子集和组合的区别:子集加入每一个节点而组合加入每一个叶子节点。
class Solution {
public:
vector<int> path;
vector<vector<int>> ans;
void backtrace(vector<int>& nums, int startID) {
ans.push_back(path);
if(startID >= nums.size()) {
return;
}
for(int i = startID; i < nums.size();++i) {
path.push_back(nums[i]);
backtrace(nums, i + 1);
path.pop_back();
}
}
vector<vector<int>> subsets(vector<int>& nums) {
backtrace(nums,0);
return ans;
}
};
90. 子集II
这题套用上一题的框架,不同点在于可加入相同的值;这类遇到重复的问题需要用到used数组帮助判定是否是数组重复;当true时说明已经加入过其他相同的值但是可以再加入,当false时说明当前数组没有加入此值,但要跳过这类情况因为会和之前的数组重复,需要剪枝。
class Solution {
public:
vector<int> path;
vector<vector<int>> ans;
void bt(vector<int> &nums, int startID,vector<bool>& used) {
ans.push_back(path);
if(startID >= nums.size()) {
return;
}
for(int i = startID; i < nums.size(); ++i) {
if(i >= 1 && nums[i] == nums[i-1] && used[i - 1] == false) {
continue;
}
path.push_back(nums[i]);
used[i] = true;
bt(nums, i + 1, used);
used[i] = false;
path.pop_back();
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<bool> used(nums.size(),false);//true:树枝重复;false:数层重复
sort(nums.begin(),nums.end());
bt(nums, 0, used);
return ans;
}
};