39. 组合总和
回溯三部曲
1.确定回溯函数返回值和参数
返回值为void,参数为给定数组candidates,目标值target,和当前开始遍历的数组下标?
2.确定回溯终止条件
当前和大于目标值,直接中止此次回溯。或当相加的数字之和等于目标值target,将目标集合加入结果集,中止此次回溯。
3.确定回溯单层过程
从当前开始遍历的数组下标开始遍历剩余的数组元素,将遍历到的元素加到和上,进入下一个递归层。递归出来之后进行回溯,减去当前遍历到的元素值
能否有剪枝操作?
可以,先将candidates数组排序后,将sum大于目标值中止递归放在for循环中,就少进递归层的次数。
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> item = new ArrayList<>();
int sum = 0;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates, target, 0);
return res;
}
private void backtracking(int[] candidates, int target, int startIndex){
if(sum == target){
res.add(new ArrayList<>(item));
return;
}
for(int i = startIndex; i < candidates.length; i++){
if(sum + candidates[i] > target){
break;
}
item.add(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, i);
item.remove(item.size() - 1);
sum -= candidates[i];
}
}
}
——————————————————————————————————————————
40. 组合总和 II
本题有重复的元素,需要去重
去重之前要先对candidates数组进行排序
回溯三部曲
1.确定回溯函数返回值和参数
返回值为void,参数为给定数组candidates,目标值target,和当前开始遍历的数组下标
2.确定回溯终止条件
当前和大于目标值,直接中止此次回溯。或当相加的数字之和等于目标值target,将目标集合加入结果集,中止此次回溯。
3.确定回溯单层过程
从当前开始遍历的数组下标开始遍历剩余的数组元素,将遍历到的元素加到和上,进入下一个递归层。递归出来之后进行回溯,减去当前遍历到的元素值
能否有剪枝操作?
可以,先将candidates数组排序后,将sum大于目标值中止递归放在for循环中,就少进递归层的次数。
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> item = new ArrayList<>();
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backtracking(candidates, target, 0);
return res;
}
private void backtracking(int[] candidates, int target, int startIndex){
if(sum == target){
res.add(new ArrayList<>(item));
return;
}
for(int i = startIndex; i < candidates.length; i++){
//去重
if(i > startIndex && candidates[i] == candidates[i - 1]){
continue;
}
if(sum + candidates[i] > target){
break;
}
item.add(candidates[i]);
sum += candidates[i];
backtracking(candidates, target, i + 1);
item.remove(item.size() - 1);
sum -= candidates[i];
}
}
}
——————————————————————————————————————————
131. 分割回文串
可以使用动态规划来判断子串是否是回文子串
具体做法:二维dp数组isPalindrome[i][j]表示从i到j的子串是否是回文子串
当[i+1,j-1]是回文子串时,如果s[i]和s[j]相等,[i,j]是回文子串,否则不是
当[i+1,j-1]不是回文子串时,[i,j]不是回文子串
dp数组的遍历顺序:因为要从[i+1,j-1]到[i,j],所以遍历顺序i从大到小,j从小到大
本题有重复的元素,需要去重
去重之前要先对candidates数组进行排序
回溯三部曲
1.确定回溯函数返回值和参数
返回值为void,参数为给定原字符串s,和当前开始遍历的字符串字符下标
2.确定回溯终止条件
当遍历开始下标超出字符串长度时,中止递归
3.确定回溯单层过程
从当前层开始位置开始向后遍历,判断[start,end]的子串是否是回文子串,是的话,加入过程集合,进入下一层递归层。
记得回溯
class Solution {
List<List<String>> res = new ArrayList<>();
List<String> item = new ArrayList<>();
boolean[][] isPalindrome;
public List<List<String>> partition(String s) {
isPalindrome = new boolean[s.length()][s.length()];
for(int i = 0; i < s.length(); i++){
Arrays.fill(isPalindrome[i], false);
}
isPalindrome(s);
backtracking(s, 0);
return res;
}
private void backtracking(String s, int startIndex){
if(startIndex >= s.length()){
res.add(new ArrayList<>(item));
return;
}
for(int i = startIndex; i < s.length(); i++){
//if(!isPalindrome(s, startIndex, i)){
if(!isPalindrome[startIndex][i]){
continue;
}
item.add(s.substring(startIndex, i + 1));
backtracking(s, i + 1);
item.remove(item.size() - 1);
}
}
//基础的回文子串判断方法
private boolean isPalindrome(String s, int statIndex, int endIndex){
while(endIndex > statIndex){
if(s.charAt(endIndex) != s.charAt(statIndex)){
return false;
}
endIndex--;
statIndex++;
}
return true;
}
//通过动态规划判断回文子串
private void isPalindrome(String s){
for(int i = s.length() - 1; i >= 0; i--){
for(int j = 0; j < s.length(); j++){
if(i == j) isPalindrome[i][j] = true;
if(i + 1 == j) isPalindrome[i][j] = s.charAt(i) == s.charAt(j);
else if (i < j) {
isPalindrome[i][j] = isPalindrome[i + 1][j - 1] && (s.charAt(i) == s.charAt(j));
}
}
}
}
}