No.1
题目
组合总和
思路
- 回溯法
- 不限制数字使用次数,如何处理?
- 横向遍历树时,还需要使用
startIndex,以避免对同一个组合重复多次搜索
- 但是在递归下一层时,参数中不需要
startIndex + 1,表示允许从startIndex开始搜索
- 数字集中出现
0会怎么样?
- 考虑给空的
candidates数组,怎么处理
递归分析
- 全局变量:
path,result
- 返回值:空,参数:可用数字集,
target,startIndex
- 终止条件
- 路径累计值和
target相等,存储结果到result,返回
- 路径累计值已经大于
target,返回(剪枝)
路径走到尽头(耗尽所有可用数字),不存在,数字不限制使用次数
- 单层递归逻辑:
- 处理当前节点
- 递归进入下一层搜索
- 回溯,抛出当前节点
代码
private List<Integer> path;
private List<List<Integer>> result;
public void combineHelper(int[] candidates, int target, int startIndex) {
if (target == 0) {
result.add(new ArrayList<>(path));
return;
} else if (target < 0) { // 已经超过了target,没有必要继续搜索这一分支
return;
}
for (int i = startIndex; i < candidates.length; i++) {
path.add(candidates[i]);
target -= candidates[i];
combineHelper(candidates, target, i); // 不限制单个数字的使用次数,就体现在这里
target += candidates[i];
path.remove(path.size() - 1);
}
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
path = new ArrayList<>();
result = new ArrayList<>();
if (candidates.length == 0)
return result;
combineHelper(candidates, target, 0);
return result;
}
No.2
题目
组合总和 II
思路
- 回溯法,大体和No.1类似
- 这道题目和组合总和如下区别
- 本题数组
candidates的元素是有重复的,而组合总和是无重复元素的数组candidates
- 新要求:解集不能包含重复的组合,怎么处理?
- 先对
candidates排序,然后在横向遍历时,判断与path末尾的数字是否相同,相同则continue,跳过这个数字
递归分析
- 全局变量:
path,result
- 返回值:空,参数:可用数字集,
target,startIndex
- 终止条件
- 路径累计值和
target相等,存储结果到result,返回
- 路径累计值已经大于
target,返回(剪枝)
- 路径走到尽头(耗尽所有可用数字),返回(剪枝)
- 单层递归逻辑:
- 处理当前节点
- 递归进入下一层搜索
- 回溯,抛出当前节点
代码
private List<Integer> path;
private List<List<Integer>> result;
public void combineHelper(int[] candidates, int target, int startIndex) {
if (target == 0) {
result.add(new ArrayList<>(path));
return;
} else if (target < 0) {
return;
}
if (target > 0 && startIndex >= candidates.length)
return;
for (int i = startIndex; i < candidates.length; i++) {
int num = candidates[i];
// 在这里去除重复结果
if (i > startIndex && num == candidates[i - 1])
continue;
path.add(num);
target -= num;
combineHelper(candidates, target, i + 1); // 和不限制元素使用次数情况不同
target += num;
path.remove(path.size() - 1);
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
path = new ArrayList<>();
result = new ArrayList<>();
if (candidates.length == 0)
return result;
// 先对candidates排序
Arrays.sort(candidates);
combineHelper(candidates, target, 0);
return result;
}
No.3
题目
分割回文串
思路
- 先写一个判断回文串的函数
- 回溯法
- 这是切割问题,类似组合问题
递归分析
- 全局变量:
path,result
- 返回值:空,参数:原生字符串,
splitIndex
- 终止条件
- 字符串用光了,存储结果,返回
- 单层递归逻辑:
- 处理当前节点
- 割下来的字符串是回文串,进入下一层递归
- 回溯,抛出当前节点
代码
public boolean judgeReversal(String s) {
int len = s.length();
// left, right时刻同步变化,所以写在for条件里面
for (int left = 0, right = len - 1;left <= right ; left++, right--) {
if (s.charAt(left) != s.charAt(right))
return false;
}
return true;
}
private List<String> path;
private List<List<String>> result;
public void partHelper(String s, int startIndex) { // before, not included
if (startIndex >= s.length()) {
result.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < s.length(); i++) {
String subStr = s.substring(startIndex, i + 1);
path.add(subStr);
if (judgeReversal(subStr))
partHelper(s, i + 1);
path.remove(path.size() - 1);
}
}
public List<List<String>> partition(String s) {
path = new ArrayList<>();
result = new ArrayList<>();
partHelper(s, 0);
return result;
}