LeetCode 39. 组合总和
题目链接:https://leetcode.cn/problems/combination-sum/description/
文章链接:https://programmercarl.com/0039.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8C.html
思路
此题要求:candidates 中的数字可以无限制重复被选取。
注意图中叶子节点的返回条件,因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!
本题还需要startIndex来控制for循环的起始位置,对于组合问题,什么时候需要startIndex呢?
如果是一个集合来求组合的话,就需要startIndex。
如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex
private List<List<Integer>> res;
private LinkedList<Integer> path;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
res = new ArrayList<>();
path = new LinkedList<>();
backtracking(candidates, target, 0, 0);
return res;
}
void backtracking(int[] candidates, int target, int startIndex, int sum) {
if (sum == target) {
res.add(new LinkedList<>(path));
return;
}
if (sum > target) {
return;
}
for (int i = startIndex; i < candidates.length; i++) {
path.add(candidates[i]);
// 关键点:不用i+1了,表示可以重复读取当前的数
backtracking(candidates, target, i, sum + candidates[i]);
path.removeLast();
}
}
LeetCode 40.组合总和II
题目链接:https://leetcode.cn/problems/combination-sum-ii/description/
文章链接:https://programmercarl.com/0040.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CII.html
思路
解集不能包含重复的组合, 本题重点在于去重操作
此题还需要加一个int型数组used,用来记录同一树枝上的元素是否使用过,used[i]=1说明这个元素被使用过,used[i]=0说明没有被使用过。
前提是数组先排好序
如果candidates[i] == candidates[i - 1] 并且 used[i - 1] == false,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。
为什么 used[i - 1] == false 就是同一树层呢,因为同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。
private List<List<Integer>> result = new ArrayList<>();
private LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
int[] used = new int[candidates.length];
backtracking(candidates,target,0,0,used);
return result;
}
void backtracking(int[] candidates, int target, int sum, int startIndex, int[] used) {
if (sum == target) {
result.add(new LinkedList<>(path));
}
if (sum > target)
return;
for (int i = startIndex; i < candidates.length; i++) {
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == 0)
continue;
path.add(candidates[i]);
used[i] = 1;
backtracking(candidates, target, sum + candidates[i], i + 1,used);
used[i] = 0;
path.removeLast();
}
}
LeetCode 131.分割回文串
题目链接:https://leetcode.cn/problems/palindrome-partitioning/description/
文章链接:https://programmercarl.com/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
思路
所以切割问题,也可以抽象为一棵树形结构,如图:
从树形结构的图中可以看出:切割线切到了字符串最后面,说明找到了一种切割方法,此时就是本层递归的终止条件。
private List<List<String>> result = new ArrayList<>();
private List<String> path = new ArrayList<>();
public List<List<String>> partition(String s) {
backTracking(s,0);
return result;
}
public void backTracking(String s, int startIndex) {
// 当切割线切割到最后的时候
if (startIndex >= s.length()) {
result.add(new ArrayList<>(path));
return;
}
// 单层搜索
for (int i = startIndex; i < s.length(); i++) {
// 子串范围[startIndex,i]
// 判断是否是回文,是回文加入path
if (palindrome(s.substring(startIndex, i + 1))) {
path.add(s.substring(startIndex, i + 1));
} else {
continue;
}
backTracking(s, i + 1);
// 回溯
path.remove(path.size() - 1);
}
}
private boolean palindrome(String s) {
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)){
return false;
}
}
return true;
}