复原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 地址。
示例 1:
- 输入:s = “25525511135”
- 输出:[“255.255.11.135”,“255.255.111.35”]
示例 2:
- 输入:s = “0000”
- 输出:[“0.0.0.0”]
示例 3:
- 输入:s = “1111”
- 输出:[“1.1.1.1”]
示例 4:
- 输入:s = “010010”
- 输出:[“0.10.0.10”,“0.100.1.0”]
示例 5:
- 输入:s = “101023”
- 输出:[“1.0.10.23”,“1.0.102.3”,“10.1.0.23”,“10.10.2.3”,“101.0.2.3”]
提示:
- 0 <= s.length <= 3000
- s 仅由数字组成
和回文串切割问题一样的切割问题,分割线还是用startIndex;
对于这个字符串,从首位开始切割直到切割的内容不合法然后停止切割,然后进入下层递归(树的深度),操作完成后继续for循环向右(树的宽度),如图:
例如 256147,有:
2.56.1.47 2.56.14.7 25.6.1.47 25.6.14.7 25.61.4.7
此处可以剪枝的条件有很多,只要切割的内容不合法了,那此树层的后序节点都不用再继续切割;
vector<string> res;
void backtracking(const string& s, int startIndex, int pointSum){//'.'的数量为3,决定树的深度
if(pointSum == 3){
if(isValid(s, startIndex, s.size()-1)){//左闭右闭
res.push_back(s);//修改string加入'.'的过程放入单层递归逻辑
}
return;
}
for(int i = startIndex; i < s.size(); i++){//
if(!(isValid(s, startIndex, i))){
continue;
}
s.insert(s.begin() + i+ 1,'.');//因为库函数中insert是插入到下标后面
pointSum++;
backtracking(s, i + 2, pointSum);//因为插入了'.'
s.erase(s.begin() + i + 1);
pointSum--;
}
}
bool isValid(const string& s, int begin, int end){
if(begin > end) return false;//合法判断,其实这里可以不写
if(s[begin] == '0' && begin != end) return false;//0开头但不止一位,非法
int num = 0;
for(int i = begin; i <= end; i++){
if(s[i] > '9' || s[i] < '0'){//非数字字符,非法
return false;
}
num = 10*num + (s[i] - '0');
if(num > 255) return false;//超出范围,非法
}
return ture;
}
整体代码如下:
class Solution {
private:
vector<string> res;
void backtracking(string& s, int startIndex, int pointSum){//'.'的数量为3,决定树的深度
if(pointSum == 3){
if(isValid(s, startIndex, s.size()-1)){//左闭右闭
res.push_back(s);//修改string加入'.'的过程放入单层递归逻辑
}
return;
}
for(int i = startIndex; i < s.size(); i++){//
if(!(isValid(s, startIndex, i))){
continue;
}
s.insert(s.begin() + i+ 1,'.');//因为库函数中insert是插入到下标后面
pointSum++;
backtracking(s, i + 2, pointSum);//因为插入了'.'
s.erase(s.begin() + i + 1);//回溯
pointSum--;//回溯
}
}
bool isValid(const string& s, int begin, int end){
if(begin > end) return false;//合法判断,其实这里可以不写
if(s[begin] == '0' && begin != end) return false;//0开头但不止一位,非法
int num = 0;
for(int i = begin; 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<string> restoreIpAddresses(string s) {
res.clear();
if (s.size() < 4 || s.size() > 12) return res; // 剪枝
backtracking(s ,0, 0);
return res;
}
};
子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
一样是回溯算法树形结构,且是和组合问题一样的切割方法,但是区别在于子集问题是将所有节点全部放入结果集;
如图:
(求组合问题时i依然是从startIndex开始,求排列问题的时候才是从i=0开始遍历,因为排列要求有序)
则每进入一层递归,都要将此层递归收集到的结果放进结果集;
vector<vector<int>> res;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex){
res.push_back(path);
//if(startIndex >= nums.size()) return; 因为for循环的条件和此处终止条件判定一致
for(int i = startIndex; i < nums.size(); i++){
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
return;
}
子集Ⅱ
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
- 输入: [1,2,2]
- 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
和上题的区别在于,此处的子集是有重复元素的;
有重就去重呗,排序数组,然后判断树层是否重复;
vector<vector<int>> res;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex){
res.push_back(path);
for(int i = startIndex; i < nums.size(); i++){
if(i > startIndex && nums[i] == nums[i - 1]) continue;
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
return;
}
整体代码如下:
class Solution {
private:
vector<vector<int>> res;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex){
res.push_back(path);
for(int i = startIndex; i < nums.size(); i++){
if(i > startIndex && nums[i] == nums[i - 1]) continue;//参考之前的组合总和Ⅱ
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
return;
}
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
res.clear();
path.clear();
sort(nums.begin(),nums.end());
backtracking(nums, 0);
return res;
}
};