网站建设推广军事新闻 今日关注
8.复原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
中的任何数字。你可以按 任何 顺序返回答案。
其实本题与上一题很像,都是用分割线在所给字符串里进行切割,本题,如果切割到>255的情况就得终止,以及题目要求中ip地址只有四段,即出现三个逗号时就得终止
class Solution {
public:vector<string>result;//由题,本题返回的结果是由字符串组成的一维数组//判断是否为合法ip地址bool isValid(const string & s,int start,int end){if (start > end) {return false;}if(s[start] == '0' && start!=end )//整数可以是单个0,但是不能是由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;}}return true;}void backtracking(string& s,int startIndex,int pointSum){//终止条件if(pointSum==3){if(isValid(s,startIndex,s.size()-1)==true){result.push_back(s); } return;}
for(int i=startIndex;i<s.size();i++){if(isValid(s,startIndex,i)==true)//判断子区间是否为有效{s.insert(s.begin() + i + 1 , '.'); // 在i的后面插入一个逗点cout<<pointSum;pointSum++;backtracking(s,i+2,pointSum);//因为加了逗号占一个位置,所以应该传入i+2pointSum--;//回溯s.erase(s.begin()+i+1);//回溯}else{//不合法break;}}}vector<string> restoreIpAddresses(string s) {result.clear();if(s.size()<4 || s.size()>12)return result;//剪枝backtracking(s,0,0);return result;}
};
疑惑点:
bool isValid(const string & s,int start,int end) { if (start > end) { return false; }
为什么要加上这个判断,不加上的话,"101023"这个示例就过不去
分析原因:因为 backtracking(s,i+2,pointSum);这里的i是两个两个加
** 全过程 **
初始条件
输入:
"101023"
我们调用restoreIpAddresses
方法,它会执行以下步骤:
初始化
result
调用
backtracking(s, startIndex=0, pointSum=0)
。
递归模拟
第一次递归 (初始状态)
startIndex = 0, pointSum = 0
遍历
i
从startIndex=0
开始,尝试插入.
。子循环:
i=0
子区间:
"1"
(合法,isValid(0,0)
返回true
)。插入
.
:s = "1.01023"
,pointSum++ = 1
。递归调用
backtracking(s, startIndex=2, pointSum=1)
。
第二次递归
startIndex = 2, pointSum = 1
遍历
i
从startIndex=2
开始,尝试插入.
。子循环:
i=2
子区间:
"0"
(合法,isValid(2,2)
返回true
)。插入
.
:s = "1.0.1023"
,pointSum++ = 2
。递归调用
backtracking(s, startIndex=4, pointSum=2)
。
第三次递归
startIndex = 4, pointSum = 2
遍历
i
从startIndex=4
开始,尝试插入.
。子循环:
i=4
子区间:
"1"
(合法,isValid(4,4)
返回true
)。插入
.
:s = "1.0.1.023"
,pointSum++ = 3
。递归调用
backtracking(s, startIndex=6, pointSum=3)
。
第四次递归(出现问题的地方)
startIndex = 6, pointSum = 3
此时,
pointSum == 3,需要检查剩下的子区间是否合法。
调用
isValid(s, startIndex=6, end=5)
(这里的end=5
是s.size()-1
)。问题:
startIndex=6
,但end=5
,导致start > end
。
9.子集
给你一个整数数组
nums
,数组中的元素 互不相同 。返回该数组所有可能的子集
(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]示例 2:
输入:nums = [0] 输出:[[],[0]]
由上图可知,本题与之前的回溯问题最大的区别就是,之前的回溯算法题收集的都是叶子节点,但是本题收集的是除根结点外所有结点
class Solution {
public:vector<int>path;vector<vector<int>>result;void backtracking(vector<int>&nums,int startIndex){result.push_back(path);//收集空集if(startIndex>=nums.size())//终止条件{return;}
for(int i = startIndex;i<nums.size();i++){path.push_back(nums[i]);backtracking(nums,i+1);path.pop_back();}}vector<vector<int>> subsets(vector<int>& nums) {path.clear();result.clear();backtracking(nums,0);return result;}
};
无剪枝操作,因为本题需要遍历整个树,并记录每个结点
10.子集问题II
给你一个整数数组
nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
这题升级的点在于去重操作
思路:效仿组合总和II先将数组进行排序后再传入
class Solution {
public:vector<int>path;vector<vector<int>>result;void backtracking(vector<int>&nums,int startIndex,vector<bool>&used){result.push_back(path);if(startIndex>=nums.size()){return;}for(int i=startIndex;i<nums.size();i++){if(i>0 && nums[i]==nums[i-1] && used[i-1]==false)//相同元素会产生的结果已经收集过了{continue;}path.push_back(nums[i]);used[i]=true;backtracking(nums,i+1,used);used[i]=false;//回溯path.pop_back();}}vector<vector<int>> subsetsWithDup(vector<int>& nums) {path.clear();result.clear();vector<bool>used(nums.size(),false);sort(nums.begin(),nums.end());backtracking(nums,0,used);return result;}
};