学习目标:
- 39. 组合总和
- 40.组合总和II
- 131.分割回文串
学习内容:
39. 组合总和
思路是在数组中按顺序抽取数值相加,若大于tar就舍去
因为给的数组一开始是乱的,所以用arrays.sort方法进行排序再加入到回溯方法中,将判断sum和tar的大小放在遍历中(优化部分),回溯三步 将数值加入到path数组中 进入下一层循环 删除数组最后一个数值(回溯)
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(candidates); // 先进行排序
backtracking(res, new ArrayList<>(), candidates, target, 0, 0);
return res;
}
public void backtracking(List<List<Integer>> res, List<Integer> path, int[] candidates, int target, int sum, int idx) {
// 找到了数字和为 target 的组合
if (sum == target) {
res.add(new ArrayList<>(path));
return;
}
for (int i = idx; i < candidates.length; i++) {
// 如果 sum + candidates[i] > target 就终止遍历
if (sum + candidates[i] > target) break;
path.add(candidates[i]);
backtracking(res, path, candidates, target, sum + candidates[i], i);
path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素
}
}
}
40.组合总和II
与上一题要求的区别是给的数组中的数值不能重复使用
思路:使用标记数组表明是否使用过(boolean类的数组,使用过就是true,没使用过就是false) ,碰到使用过的就跳过,将去重操作放在遍历过程中
与上一题的细节在于该题中的元素只能使用一次,所以在套下一层时将开始序数startindex+1
使用标记数组
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> ans = new ArrayList<>();
boolean[] used;
int sum = 0;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
used = new boolean[candidates.length];
// 加标志数组,用来辅助判断同层节点是否已经遍历
Arrays.fill(used, false);
// 为了将重复的数字都放到一起,所以先进行排序
Arrays.sort(candidates);
backTracking(candidates, target, 0);
return ans;
}
private void backTracking(int[] candidates, int target, int startIndex) {
if (sum == target) {
ans.add(new ArrayList(path));
}
for (int i = startIndex; i < candidates.length; i++) {
if (sum + candidates[i] > target) {
break;
}
// 出现重复节点,同层的第一个节点已经被访问过,所以直接跳过
if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
sum += candidates[i];
path.add(candidates[i]);
// 每个节点仅能选择一次,所以从下一位开始
backTracking(candidates, target, i + 1);
used[i] = false;
sum -= candidates[i];
path.removeLast();
}
}
}
131.分割回文串
思路:分三部分 方法实现 分割部分 判断回文串部分
分割部分:从头开始分割,若左边部分是回文数就将左边放入deque中,起始位置往右移一位继续切割,若不是回文数就将切割位置往右移一位继续判断回文数以此类推,当起始位置比s长度大时说明已经到不能切割的位置,将得到的集合放在答案集中
判断部分:即将字符串的最左和最右字母进行对比,若一致就进入下一个循环,左右指针各往内移一位进行对比......若出现两字母不匹配说明不是回文数返回false,若循环到左指针比右指针大时说明是回文数返回true
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;
}
}