39. 组合总和
自己想的代码对,但单层搜索逻辑不够清晰,别背,注意回溯index的起始如果是从数组里面挑从0开始,而不是前面的题1~9从1开始。
//C++
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
BackTracking(target, candidates, 0, 0);
return result;
}
void BackTracking(int target, vector<int>& candidates, int index, int sum) {
if (sum > target) return;
if (sum == target) {
result.push_back(path);
return;
}
for (int i = index; i < candidates.size(); i++) {
sum += candidates[i];
path.push_back(candidates[i]);
BackTracking(target, candidates, i, sum);//核心不用i+1,因为可以选自己
sum -= candidates[i];
path.pop_back();
}
}
};
//C#
public class Solution {
IList<IList<int>> res = new List<IList<int>>();
List<int> path = new List<int>();
public IList<IList<int>> CombinationSum(int[] candidates, int target) {
BackTracking(candidates, target, 0, 0);
return res;
}
public void BackTracking(int[] candidates, int target, int sum, int startIndex) {
if (sum > target) return;
if (sum == target) {
res.Add(new List<int>(path));
return;
}
for (int i = startIndex; i < candidates.Length ; i++) {
sum += candidates[i];
path.Add(candidates[i]);
BackTracking(candidates, target, sum, i);
sum -= candidates[i];
path.RemoveAt(path.Count - 1);
}
}
}
可以在递归的上一层就判断当前和+下一层最小的节点(就是当前层的i)>target,剪枝,但前提要先排序,这时候原来的sum > target就可以省去了。
//C#
public class Solution {
IList<IList<int>> res = new List<IList<int>>();
List<int> path = new List<int>();
public IList<IList<int>> CombinationSum(int[] candidates, int target) {
Array.Sort(candidates);
BackTracking(candidates, target, 0, 0);
return res;
}
public void BackTracking(int[] candidates, int target, int sum, int startIndex) {
//if (sum > target) return;
if (sum == target) {
res.Add(new List<int>(path));
return;
}
for (int i = startIndex; i < candidates.Length && sum + candidates[i] <= target; i++) {
sum += candidates[i];
path.Add(candidates[i]);
BackTracking(candidates, target, sum, i);
sum -= candidates[i];
path.RemoveAt(path.Count - 1);
}
}
}
- 找到结果值了别忘了return!
- 不同集合之间的组合用index,同一个集合之间的组合用startIndex。
- startIndex + 1代表不可以重复用数组中同一个位置的元素,startIndex代表可以重复用元素。
- C#记得path添加进去要new出来个对象,不像C+可以自动拷贝。
40. 组合总和II
数组candidates有重复元素,但还不能有重复的组合,要在树层去重而不是树枝去重,先排序让相邻的元素在一块。
为了区分树枝和树层去重(相同元素集合组合才会有这种区别),要看一下used[i - 1],为false则表示是树层去重(回溯限制如果不是树枝那一定这条路没走过i-1为false),因为为true表示上一个数用过了,那就是树枝重复的情况,是允许的。
//C#
public class Solution {
public IList<IList<int>> res = new List<IList<int>>();
public List<int> path = new List<int>();
public IList<IList<int>> CombinationSum2(int[] candidates, int target) {
bool[] used = new bool[candidates.Length];
Array.Sort(candidates);
BackTracking(candidates, target, 0, 0, used);
return res;
}
public void BackTracking(int[] candidates,int target, int sum, int startIndex, bool[] used) {
if (sum == target) {
res.Add(new List<int>(path));
return;
}
for (int i = startIndex; i < candidates.Length && sum + candidates[i] <= target; i++) {
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
continue;
}
sum += candidates[i];
path.Add(candidates[i]);
used[i] = true;
BackTracking(candidates, target, sum, i + 1, used);
used[i] = false;
sum -= candidates[i];
path.RemoveAt(path.Count - 1);
}
}
}
也可以通过startInde区分是树层还是树枝,只有当candidates[i] == candidates[i - 1],并且i是当前层第一个点的时候才有可能是树枝去重,其余都是树层去重(for循环第二个节点就进到层的逻辑了,i向层内进行了移动)。
-
使用startindex去重的逻辑:是否是相同数值的第一个树枝,如果不是,则去掉。条件设置为i != index更容易理解些感觉
-
使用used去重的逻辑:遍历到当前节点时,是否还在同一个树枝上,如果不是,则去掉。
//C++
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end());
BackTracking(candidates, target, 0, 0);
return res;
}
void BackTracking(vector<int> & candidates, int target, int sum, int startIndex) {
if (sum > target) return;
if (sum == target) {
res.push_back(path);
return;
}
for (int i = startIndex; i < candidates.size() ; i++) {
if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
sum += candidates[i];
path.push_back(candidates[i]);
BackTracking(candidates, target, sum, i + 1);
sum -= candidates[i];
path.pop_back();
}
}
};
131.分割回文串
字符串切割的位置(在路径选择的字符之后)就等同于树的路径。startIndex就是切割线的位置,因此startIndex == string.size 切割完毕。
- 当前层需要判断的是[startIndex, i]是不是回文子串。
- C+的substr切分函数和C#对应的注意下。
- 注意这里到终止条件肯定是符合条件的(分割肯定是要到头的,不存在路径没到叶子节点提前返回),不符合在for里已经continue排除了,和前面几道题有所不同。
//C++
class Solution {
public:
vector<vector<string>> res;
vector<string> path;
vector<vector<string>> partition(string s) {
BackTracking(s, 0);
return res;
}
void BackTracking(string &s, int startIndex) {
if (startIndex == s.size()) {
res.push_back(path);
return;
}
for (int i = startIndex; i < s.size(); i++) {
if(doit(s,startIndex, i)) {
string str = s.substr(startIndex, i - startIndex + 1);
path.push_back(str);
BackTracking(s, i + 1);
path.pop_back();
} else {
continue;
}
}
}
bool doit(string s, int left, int right) {
while (left <= right) {
if (s[left] == s[right]) {
left++;
right--;
} else return false;
}
return true;
}
};
//C#
public class Solution {
public IList<IList<string>> res = new List<IList<string>>();
public List<string> path = new List<string>();
public IList<IList<string>> Partition(string s) {
BackTracking(s, 0);
return res;
}
public void BackTracking(string s, int startIndex) {
if (startIndex == s.Length) {
res.Add(new List<string>(path));
return;
}
for (int i = startIndex; i < s.Length; i++) {
string sub = s.Substring(startIndex, i - startIndex + 1);
if (isHuiWen(sub, 0, sub.Length - 1)) {
path.Add(sub);
BackTracking(s, i + 1);
path.RemoveAt(path.Count - 1);
} else continue;
}
}
public bool isHuiWen(string s, int left, int right) {
for (int i = left, j = right; i <= j; i++, j--) {
if (s[i] != s[j]) return false;
}
return true;
}
}