组合总和
题目链接
代码:
class Solution {
public List<List<Integer>> res = new ArrayList<>();
public List<Integer> list = new ArrayList<>();
public int sum = 0;
/**
* @param nums 目标数组
* @param target 目标和
* @param start 起始位置
*/
public void dfs(int[] nums, int target, int start) {
//sum>=target就返回
if (sum > target) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(list));
return;
}
for (int i = start; i < nums.length; i++) {
list.add(nums[i]);//处理
sum += nums[i];
dfs(nums, target, i);//递归,注意这里是i(表示可以重复读取当前的数)
list.remove(list.size() - 1);//回溯
sum -= nums[i];
}
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(candidates, target, 0);
return res;
}
}
组合总和II
题目链接
本题的难点在于区别2中:集合(数组candidates)有重复元素,但还不能有重复的组合。
需要对同一层上的用过的数进行去重
如果candidates[i] == candidates[i - 1]
并且 used[i - 1] == false
,就说明:前一个树枝,使用了candidates[i - 1],也就是说同一树层使用过candidates[i - 1]。
此时for循环里就应该做continue的操作。
这块比较抽象,如图:
我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:
- used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
- used[i - 1] == false,说明同一树层candidates[i - 1]使用过
可能有的录友想,为什么 used[i - 1] == false 就是同一树层呢,因为同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。
而 used[i - 1] == true,说明是进入下一层递归,去下一个数,所以是树枝上,如图所示:
代码:
class Solution {
public List<List<Integer>> res = new ArrayList<>();
public List<Integer> list = new ArrayList<>();
public int sum = 0;
/**
* @param nums 目标数组
* @param target 目标和
* @param start 起始位置
*/
public void dfs(int[] nums, int target, int start) {
//sum>=target就返回
if (sum > target) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(list));
return;
}
for (int i = start; i < nums.length; i++) {
if ( i > start && nums[i] == nums[i - 1] ) {//同一层不能重复取
continue;
}
list.add(nums[i]);//处理
sum += nums[i];
dfs(nums, target, i + 1);//递归
int temp=list.get(list.size()-1);
sum-=temp;
list.remove(list.size() - 1);//回溯
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates, target, 0);
return res;
}
}
分割回文串
题目链接
利用回溯处理切割问题,
所以切割问题,也可以抽象为一棵树形结构,如图:
递归用来纵向遍历,for循环用来横向遍历,切割线(就是图中的红线)切割到字符串的结尾位置,说明找到了一个切割方法。
代码:
class Solution {
public List<List<String>> res=new ArrayList<>();
public List<String> list=new ArrayList<>();
public boolean cheak(String s,int start,int end){
for (int i = start,j=end; i <j ; i++,j--) {
if(s.charAt(i)!=s.charAt(j)){
return false;
}
}
return true;
}
public void dfs(String s,int startIndex) {
if (startIndex >= s.length()) {
res.add(new ArrayList(list));
return;
}
for (int i = startIndex; i < s.length(); i++) {
//如果是回文子串,则记录
if (cheak(s, startIndex, i)) {
String str = s.substring(startIndex, i + 1);
list.add(str);
} else {
continue;
}
//起始位置后移,保证不重复
dfs(s, i + 1);
list.remove(list.size() - 1);
}
}
public List<List<String>> partition(String s) {
dfs(s, 0);
return res;
}
}