回溯算法part2
回溯算法part2
39. 组合总和 - 力扣(LeetCode)
本题中,回溯函数参数会使用startIndex,那么,这个参数应该何时有何时无呢?
如果是一个集合来求组合的话,就需要startIndex,例如:77.组合 (opens new window),216.组合总和III (opens new window)。
如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母组合
startIndex起到作用就是去重
代码如下:
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> 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 ArrayList<>(path));
return ;
}
for(int i = startIndex; i < Math.min(150, candidates.length); i++){
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, sum, i);
path.removeLast();
sum -= candidates[i];
}
}
}
40. 组合总和 II - 力扣(LeetCode)
都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因。
本题为什么要去重?
因为,candidates数组中有相同的数字,但是输出答案数组中不能有相同的数字
所以本题需要去重,去同一树层的重
去重逻辑:
if(i > startIndex && candidates[i] == candidates[i - 1]){
continue;
}
总体代码:
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.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 ArrayList<>(path));
return ;
}
for(int i = startIndex; i < candidates.length; i++){
if(i > startIndex && candidates[i] == candidates[i - 1]){
continue;
}
path.add(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, sum, i + 1);
path.removeLast();
sum -= candidates[i];
}
}
}
131. 分割回文串 - 力扣(LeetCode)
本题类型为分割类型,可以看作类组合问题,如图所示(理解此图就理解了为什么分割类问题可以看作类组合问题)

所以本题就可以用回溯算法的模板来做了,因为是一个集合中递归(一个String s),所以要有startIndex参数控制
本题另一个难点是回文串的选取,范围就是s.substring(startIndex, i) 注意substring函数为左闭右开。
String str = s.substring(startIndex, i + 1);
另一个难点是回文串的判断,直观想法是反转判断是否==,但是双指针明显更简单
public boolean isTrueStr(String str){
for(int i = 0; i < str.length()/2; i++){
if(str.charAt(i) == str.charAt(str.length() - i - 1)){
continue;
}else{
return false;
}
}
return true;
}
总体代码如下:
class Solution {
List<List<String>> res = new ArrayList<>();
List<String> path = new ArrayList<>();
public List<List<String>> partition(String s) {
backtracking(s, 0);
return res;
}
public void backtracking(String s, int startIndex) {
//终止条件
if(startIndex == s.length()){
res.add(new ArrayList<>(path));
return ;
}
for(int i = startIndex; i < s.length(); i++){
String str = s.substring(startIndex, i + 1);
if(isTrueStr(str)){ // 是回文串
path.add(str);
}else{
continue;
}
backtracking(s, i + 1);
path.removeLast();
}
}
public boolean isTrueStr(String str){
for(int i = 0; i < str.length()/2; i++){
if(str.charAt(i) == str.charAt(str.length() - i - 1)){
continue;
}else{
return false;
}
}
return true;
}
}
浙公网安备 33010602011771号