一、复原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 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
思路如下:
回溯三部曲:
1、确定函数参数和返回值:需要一个全局变量result数组来收集最后的结果。
pointsum用于记录字符串中的.的个数
start相当于分割线,记录起始的位置。
2、确定终止条件:当pointsum的个数等于3时,需要判断最后一个.到末尾的字符串是否是合法子串,如果是,则加入到结果容器里。
3、单层逻辑。
子串的范围是start到i,如果子串是有效的,那么就在指定位置加入.,调用insert函数,注意是在指定位置的前面加入.,同时pointsum要加1,回溯的时候注意要将start传成i+2
注意:思路在判断子串是否是有效IP地址那里受阻了,我先把字符串切割出来,再用调用库函数stoi,ac不了,提示是超出了范围,后来改为long也不行。
bool isvaild(string s, int begin, int end)
{
//判断子串是否合法
string temp_str = s.substr(begin, end - begin + 1);
if (temp_str[0] == '0' && temp_str.size() > 1)
{
//0开头的数字不合法
return false;
}
if (temp_str.size() == 0)
{
return false;
}
for (int i = begin; i <= end; i++)
{
if (s[i] > '9' || s[i] < '0')
{//遇到非数字字符不合法
return false;
}
}
long temp_int = stol(temp_str);
if (temp_int < 0 || temp_int>255)
{
return false;
}
return true;
}
代码如下:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Solution {
public:
vector<string>result;//存放最后的结果
bool isvalid(string s, int start, int end)
{
if (start > end)
{
return false;
}
if (s[start] == '0' && start != end)
{
//0开头的数字不合法
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;
}
}
}
void backtracking(string s, int start, int pointsum)
{
//pointsum用于记录字符串中的.的个数
//start相当于分割线
if (pointsum == 3)
{
//终止条件,需要判断最后一个.到末尾的字符串是否是合法子串
if (isvaild(s, start, s.size() - 1))
{
result.push_back(s);
return;
}
else
{
return;
}
}
//单层逻辑
for (int i = start; i < s.size(); i++)
{
if (isvaild(s, start, i))//判断子串是否合法,再加入.号
{
s.insert(s.begin() + i + 1, '.');//在指定位置之前加入.
pointsum += 1;
backtracking(s, i + 2, pointsum);//递归
s.erase(s.begin() + i + 1);
pointsum -= 1;//回溯
}
}
}
vector<string> restoreIpAddresses(string s)
{
backtracking(s, 0, 0);
return result;
}
};
int main()
{
Solution s;
s.restoreIpAddresses("101023");
return 0;
}
运行如下:
二、子集问题
链接:力扣
描述:给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路如下:
在写终止条件上,思考了比较久,其实就是start>=num.size的时候终止,直接return,其他情况都是直接加入到结果容器里。
代码如下:
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>>result;//存放最终的结果
vector<int>temp_num;//存放单个数组的结果
//void backtracking(vector<int>& nums, int start)
//{
// if (temp_num.size()>0&& temp_num.size() <= nums.size())
// {//终止条件
// result.push_back(temp_num);
// }
// else
// {
// result.push_back(temp_num);
// }
// if (start==nums.size())
// {
// return;
// }
// //单层逻辑
// for (int i = start; i < nums.size(); i++)
// {
// temp_num.push_back(nums[i]);
// backtracking(nums, i + 1);//递归
// temp_num.pop_back();//回溯
// }
//}
vector<vector<int>> subsets(vector<int>& nums)
{
result.clear();
temp_num.clear();
backtracking(nums, 0);
return result;
}
};
int main()
{
Solution s;
vector<int>v{ 1,2,3,4 };
s.subsets(v);
return 0;
}
运行如下:
三、子集II
链接:力扣
描述:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路如下:
注意先对原数组要进行排序,再利用一个used数组来记录使用过的元素,对于重复元素的处理逻辑与之前的组合总和II思路一致,是树层去重,而不是树枝去重。
代码如下:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class Solution {
public:
vector<vector<int>>result;
vector<int>temp_num;
int used[10] = { 0 };//记录使用过的元素
void backtracking(vector<int>& nums, int start)
{
result.push_back(temp_num);
if (start >= nums.size())
{//终止条件
return;
}
//单层递归
for (int i = start; i < nums.size(); i++)
{
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == 0)
{
continue;
}
temp_num.push_back(nums[i]);
used[i]++;
backtracking(nums, i + 1);
temp_num.pop_back();//回溯
used[i]--;
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums)
{
result.clear();
temp_num.clear();
sort(nums.begin(), nums.end());
backtracking(nums, 0);
return result;
}
};
运行如下: