力扣216题、39题、40题(组合总和)
216、组合总和III
基本思想:
回溯法
具体实现:
剪枝优化:
1.元素总和大于目标值,在递归终止的地方剪枝
2.和77题思路一样,for循环的范围剪枝
代码:
class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> path = new LinkedList<>(); public List<List<Integer>> combinationSum3(int k, int n) { backTracking(n, k, 1, 0); return result; } private void backTracking(int targrtSum, int k, int startIndex, int sum){ if(sum > targrtSum){ return;//剪枝 } if (path.size() == k){ if (sum == targrtSum) { result.add(new ArrayList<>(path)); } return; } for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++){ path.add(i); sum += i; backTracking(targrtSum,k,i+1,sum); path.removeLast(); sum -= i; } } }
39、组合总和
具体实现:
代码:
class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> track = new LinkedList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { traversal(candidates, target, 0, 0); return result; } public void traversal(int[] candidates, int target, int sum, int idx) { if (sum > target) return; if (sum == target) { result.add(new ArrayList(track)); return; } for (int i = idx; i < candidates.length; i++) { sum += candidates[i]; track.add(candidates[i]); traversal(candidates, target, sum, i); sum -= candidates[i]; track.removeLast(); } } }
剪枝:
先对candidates进行从小到大的排序
本层的sum+candidates[i] >traget,就跳出循环
代码:
class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> track = new LinkedList<>(); public List<List<Integer>> combinationSum(int[] candidates, int target) { Arrays.sort(candidates); traversal(candidates, target, 0, 0); return result; } public void traversal(int[] candidates, int target, int sum, int idx) { if (sum == target) { result.add(new ArrayList(track)); return; } for (int i = idx; i < candidates.length; i++) { if (sum + candidates[i] > target) break; sum += candidates[i]; track.add(candidates[i]); traversal(candidates, target, sum, i); sum -= candidates[i]; track.removeLast(); } } }
40、组合总和II
基本思想:
与39.组合总和套路相同,此题还需要加一个bool型数组used,用来记录同一树枝上的元素是否使用过。
具体实现:
使用数组中相同的元素有两种场景,
一种是同一树枝上,这个允许相同
另一种是同一树层上,这个不允许相同,此时需要跳出这一循环,进入下一循环
如果condidate[i]=condidate[i-1],判断是同一树枝的元素相等还是同一树层的元素相同?
1.用used数组,
- used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
- used[i - 1] == false,说明同一树层candidates[i - 1]使用过
2.直接跳过同一树层中使用过的元素
代码:
class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> track = new LinkedList<>(); public List<List<Integer>> combinationSum2(int[] candidates, int target) { Arrays.sort(candidates); boolean[] used = new boolean[candidates.length]; traversal(candidates, target, 0, 0, used); return result; } public void traversal(int[] candidates, int target, int sum, int idx, boolean[] used) { if (sum == target) { result.add(new ArrayList(track)); return; } for (int i = idx; i < candidates.length; i++) { if (i > 0 && candidates[i] == candidates[i-1] && used[i-1] == false) continue; if (sum + candidates[i] > target) break; sum += candidates[i]; track.add(candidates[i]); used[i] = true; traversal(candidates, target, sum, i + 1, used); sum -= candidates[i]; track.removeLast(); used[i] = false; } } }
class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> track = new LinkedList<>(); public List<List<Integer>> combinationSum2(int[] candidates, int target) { Arrays.sort(candidates); traversal(candidates, target, 0, 0); return result; } public void traversal(int[] candidates, int target, int sum, int idx) { if (sum == target) { result.add(new ArrayList(track)); return; } for (int i = idx; i < candidates.length; i++) { //跳过同一树层使用过的元素 if (i > idx && candidates[i] == candidates[i-1]) continue; if (sum + candidates[i] > target) break; sum += candidates[i]; track.add(candidates[i]); traversal(candidates, target, sum, i + 1); sum -= candidates[i]; track.removeLast(); } } }