39.组合总和
题目:39. 组合总和 - 力扣(LeetCode)
思路:可以重复选取,这个要怎么回溯?而且是无限制的重复。每次回溯,都还是遍历当前的数组,记住数组中的最小值,一旦发现path的值加上最小值大于目标值,直接return,
尝试
class Solution {List<List<Integer>> result= new ArrayList<>();List<Integer> path = new ArrayList<>();int min = Integer.MAX_VALUE;int sum = 0;public List<List<Integer>> combinationSum(int[] candidates, int target) {for(int i : candidates){if(i < min) min = i;}backTracking(candidates,target,sum);return result;}public void backTracking(int[] candidates, int target,int sum){if(sum == target ){// result.add(path);result.add(new ArrayList<>(path));return;}if(sum > target){return;}for(int i = 0;i < candidates.length;i++){sum += candidates[i];path.add(candidates[i]);backTracking(candidates,target,sum);path.remove(path.size()-1);sum -=candidates[i]; }}
}
🍉返回结果时,要用【 result.add(new ArrayList<>(path));】
🍉我尝试中出现的问题是,会有【2,2,3】【2,3,2】这样的重复组合
答案
// 剪枝优化
class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {Arrays.sort(candidates); // 先进行排序backtracking(candidates, target, 0, 0);return res;}public void backtracking(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(candidates, target, sum + candidates[i], i);path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素}}
}
小结
🍉对于组合问题,如果是一个集合来求组合的话,就需要startIndex,多个集合取组合,各个集合之间相互不影响,那么就不用startIndex
🍉终止遍历,用break
if (sum + candidates[i] > target) break;
🍉递归函数的参数【idx】,作用就是防止组合重复,如下图,取【2】之后,可以在【2,3,5】中接着取,但是取【5】之后只能在【5,3】中接着取
40.组合总和||
题目:40. 组合总和 II - 力扣(LeetCode)
思路:跟组合总和应该是类似的,只能使用一次,注意candidate里面有重复元素,搞一个指针,不断移动地去加,这样就能保证一个元素只被用了一次,怎么实现在同一个path下移动指针,感觉现在需要两个指针
尝试
class Solution {List<List<Integer>> result = 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 result;}public void backTracking(int[] candidates, int target,int sum,int idx){if(sum == target){result.add(new ArrayList<>(path));}for(int i = idx; i < candidates.length; i++){if(sum +candidates[i] > target) break;path.add(candidates[i]);backTracking(candidates,target,sum + candidates[i],i+1);path.remove(path.size() - 1);}}
}
出现了重复组合,关键还是不知道怎么移动指针
修改(我靠,加一个if函数就AC了)
class Solution {List<List<Integer>> result = 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 result;}public void backTracking(int[] candidates, int target,int sum,int idx){if(sum == target){result.add(new ArrayList<>(path));}for(int i = idx; i < candidates.length; i++){if(sum +candidates[i] > target) break;if (i > idx && candidates[i] == candidates[i - 1]) {continue;}path.add(candidates[i]);backTracking(candidates,target,sum + candidates[i],i+1);path.remove(path.size() - 1);}}
}
答案
class Solution {List<List<Integer>> res = new ArrayList<>();LinkedList<Integer> path = new LinkedList<>();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 start ) {if ( sum == target ) {res.add( new ArrayList<>( path ) );return;}for ( int i = start; i < candidates.length && sum + candidates[i] <= target; i++ ) {//正确剔除重复解的办法//跳过同一树层使用过的元素if ( i > start && candidates[i] == candidates[i - 1] ) {continue;}sum += candidates[i];path.add( candidates[i] );// i+1 代表当前组内元素只选取一次backTracking( candidates, target, i + 1 );int temp = path.getLast();sum -= temp;path.removeLast();}}
}
小结
🍉现在明白了,【for循环】是横向遍历,【递归】是纵向遍历
🍉【for循环】时,每个数字是不能重复的,比如【1,1,2,3】,target为【3】,树层不去重,就会出现两个【1,2】
131.分割回文串
题目:131. 分割回文串 - 力扣(LeetCode)
思路:印象中回文串要用双指针,为啥感觉遍历就行,回溯干啥啊,难在回溯时,还要判断截取的内容是否回文
答案
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;}
}
小结
🍉因为对于一个集合,存在多种分割方案,所以要用回溯
🍉【subString】要传入的是(起始位置指针),以及(结尾指针+1)