39.组合总和:
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
思路: 总的来说吧,与昨天的77.组合挺像的,有不同的地方就是动态数组中的字可以被无限重复的被选取,因此只需要在startIndex上动一下手脚就好了,如下所示,传入时传的是当前的i值即可了,这样的话就可以满足重复被选取查找了,如果在for循环中i赋值的是0而不是startIndex的话,那么返回值就会多很多,将那些重复的值 [2, 2, 3] 和 [3, 2, 2] 这样的重复组合也算进去了。通过startIndex可以动态的调整每次搜索的起始位置,进而去重。
class Solution {
public:
vector<int> vec;
vector<vector<int>> res;
int sum=0;
void travelBacking(vector<int>& candidates, int target,int startIndex){
if(sum>target){
return;
}
if(sum==target){
res.push_back(vec);
return ;
}
for(int i=startIndex;i<candidates.size();i++){
vec.push_back(candidates[i]);
sum+=candidates[i];
travelBacking(candidates,target,i);//startIndex没有变化 因为题目要求的是同一个数字可以无限制重复被选取 他控制的是下一次递归循环中可以搜索的节点
sum-=candidates[i];
vec.pop_back();
}
}
public:
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
int startIndex=0;
travelBacking(candidates,target,startIndex);
return res;
}
};
93.复原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 中的任何数字。你可以按 任何 顺序返回答案。
思路: 还是那种 理清思路就不难的题目,首先读懂题目,要求是给定一个字符串,让他按照ip地址的形式返回回来,所以我们要在回溯遍历的时候判断当前的字符串是否是满足条件的字符串,而满足条件有如下三种情况:
段位以0为开头的数字不合法
段位里有非正整数字符不合法
段位如果大于255了不合法
因此可以根据要求编写判断是否满足条件的代码,之后进入回溯函数,因为要我们在字符串中加入合适的逗点,所以我用传入一个参数来返回当前的逗点数量,也用来作为终止条件使用,如果逗点数等于3代表剩下的就是子串了,可以直接进行判断是否合法,若合法便加入到结果子串并返回。
class Solution {
public:
vector<string> res;
//段位以0为开头的数字不合法
//段位里有非正整数字符不合法
//段位如果大于255了不合法
bool isVaild(string& s,int start,int 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 travelBacking(string& s,int startIndex,int pointNum){
//当到第三个逗点时代表到最后一个字符串了
if(pointNum==3){
if(isVaild(s,startIndex,s.size()-1)){
res.push_back(s);
}
}
for(int i=startIndex;i<s.size();i++){
if(isVaild(s,startIndex,i)){
s.insert(s.begin()+i+1,'.');
pointNum++;
travelBacking(s,i+2,pointNum);
pointNum--;
s.erase(s.begin()+i+1);
}
}
}
public:
vector<string> restoreIpAddresses(string s) {
travelBacking(s,0,0);
return res;
}
};
491.非递减子序列
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
思路:和子集II是很像的,子集II是不能有重复节点,这道题是将所有满足条件的子序列存入res并返回,约束不同,而且还需要对树层进行去重,已经遍历过的数不可以重复遍历(同一父节点下不可重复使用),所以用无序哈希表unordered_set进行去重。Carl哥画的图很清晰的展现了这一点,除了这一种去重条件,还有就是当前的节点值不能小于path数组中的最后的值,因为要保证增序排列,所以将不满足条件的去重,然后就是将当前节点值加入哈希表,为了下一次的去重做铺垫。
class Solution {
vector<int> path;
vector<vector<int>> res;
void travelBacking(vector<int>& nums,int startIndex){
if(path.size()>1){
res.push_back(path);
}
unordered_set<int> st;
for(int i=startIndex;i<nums.size();i++){
if(!path.empty()&&nums[i]<path.back()||st.find(nums[i])!=st.end()){
continue;
}
st.insert(nums[i]);
path.push_back(nums[i]);
travelBacking(nums,i+1);
path.pop_back();
}
}
public:
vector<vector<int>> findSubsequences(vector<int>& nums) {
travelBacking(nums,0);
return res;
}
};
46.全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
思路:本题目不难,但是第一次没做出来,是因为细节没有把控好,由题意可知,当path的大小等于传入数组大小时,将其加入到结果数组,定义一个bool类型 used数组,代表使用过什么元素,它的下标与nums数组的是对应的,所以可以明确的看出那个数组被使用过了,如下图清晰可知.
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
void travelBacking(vector<int>& nums,vector<bool>& used){
if(path.size()==nums.size()){
res.push_back(path);//**
return;
}
for(int i=0;i<nums.size();i++){
if(used[i]==true){ //**
continue;
}
used[i]=true;
path.push_back(nums[i]);
travelBacking(nums,used);
used[i]=false;
path.pop_back();
}
}
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(),false);
travelBacking(nums,used);
return res;
}
};