刷题神器
往期回顾
>【回溯算法】|代码随想录算法训练营第19天|77. 组合、216.组合总和III、17.电话号码的字母组合
题目
39. 组合总和
-
学后思路
可以使用递归三部曲解决问题,注意循环的边界,可以重复
解法1:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backtracking(0, candidates, target, 0);
return result;
}
public void backtracking(int sum, int[] candidates, int target, int startIndex) {
if (sum > target)
return;
if (sum == target) {
result.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
sum += candidates[i];
path.add(candidates[i]);
backtracking(sum, candidates, target, i);
path.removeLast();
sum -= candidates[i];
}
}
}
- 题目总结
- 该题目可以做剪枝操作,剪枝的前提是要将数组排序,然后判断内容如果大于sum的话直接跳出
40.组合总和II
- 学后思路
和组合题目总体思路类似,需要做去重处理,在树层上进行去重
解法一:
class Solution {
List<List<Integer>> result = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
// 加标志数组,用来辅助判断同层节点是否已经遍历
boolean[] used = new boolean[candidates.length];
Arrays.fill(used, false);
// 为了将重复的数字都放到一起,所以先进行排序
Arrays.sort(candidates);
backtracking(0, candidates, target, 0, used);
return result;
}
public void backtracking(int sum, int[] candidates, int target, int startIndex, boolean[] used) {
if (sum > target)
return;
if (sum == target) {
result.add(new ArrayList<>(path));
return;
}
for (int i = startIndex; i < candidates.length; i++) {
// 树层去重
if(i>0 && !used[i-1] && candidates[i] == candidates[i-1]){
continue;
}
used[i] = true;
sum += candidates[i];
path.add(candidates[i]);
backtracking(sum, candidates, target, i+1, used);
path.removeLast();
sum -= candidates[i];
used[i] = false;
}
}
}
- 题目总结
- 注意前提条件要进行排序
- 主要判断重复的逻辑 树层去重 树枝去重
- 注意used数组的含义 当前节点如果和前一个节点一样,说明前一个节点已经处理过该节点情况
131.分割回文串
- 学后思路
切割子串的切割点就是startindex,其他思路和回溯三部曲类似
解法一:
class Solution {
List<List<String>> lists = new ArrayList<>();
Deque<String> deque = new LinkedList<>();
public List<List<String>> partition(String s) {
backTracking(s, 0);
return lists;
}
private void backTracking(String s, int startIndex) {
//如果起始位置大于s的大小,说明找到了一组分割方案
if (startIndex >= s.length()) {
lists.add(new ArrayList(deque));
return;
}
for (int i = startIndex; i < s.length(); i++) {
//如果是回文子串,则记录
if (isPalindrome(s, startIndex, i)) {
String str = s.substring(startIndex, i + 1);
deque.addLast(str);
} else {
continue;
}
//起始位置后移,保证不重复
backTracking(s, i + 1);
deque.removeLast();
}
}
//判断是否是回文串
private boolean isPalindrome(String s, int startIndex, int end) {
for (int i = startIndex, j = end; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}
}
- 题目总结
- 切割过程中要注意,子串是如何获取的
- 注意回文子串区间判断边界