给一个无重复元素的正整数数组candidates和一个正整数target,找出所有和为target的唯一组合,数字可以重复选取
- class Solution {
- List<List<Integer>> res;
- public List<List<Integer>> combinationSum(int[] candidates, int target) {
- res = new ArrayList<>();
- Arrays.sort(candidates);
- backtrack(candidates,target,0,0,new ArrayList<>());
- return res;
- }
-
- public void backtrack(int[] candidates,int target,int index,int sum,List<Integer> list){
- if(sum == target){
- res.add(new ArrayList<>(list));
- return;
- }
- for(int i = index; i < candidates.length; i++){
- //if(i > 0 && candidates[i] == candidates[i-1]) continue;
- if(sum + candidates[i] > target) break;
- list.add(candidates[i]);
- backtrack(candidates,target,i,sum + candidates[i],list);
- list.remove(list.size() - 1);
- }
- }
- }
这个题,题目当中说无重复元素,每个数可以无限取,所以不存在去重的问题,注释的那一行可以去掉了
看下一个需要去重的题目:
题目和LC.39一样,唯一不同的条件是给的数组candidates含有重复元素。这个时候就需要去重了
- class Solution {
- List<List<Integer>> res;
- public List<List<Integer>> combinationSum2(int[] candidates, int target) {
- res = new ArrayList<>();
- Arrays.sort(candidates);
- backtrack(candidates,target,0,0,new ArrayList<>());
- return res;
- }
-
- public void backtrack(int[] candidates,int target,int index,int sum,List<Integer> list){
- if(target == sum){
- res.add(new ArrayList<>(list));
- return;
- }
- for(int i = index; i < candidates.length; i++){
- //在同一个for循环中,正在选当前层的元素
- //i >= index 的部分才是这一层可以选的元素,而不是i > 0的部分
- //同一层的元素不能重复而已
- if(i > index && candidates[i] == candidates[i-1]) continue;
- if(sum + candidates[i] > target) break;
- list.add(candidates[i]);
- backtrack(candidates,target,i+1,sum + candidates[i],list);
- list.remove(list.size() - 1);
- }
- }
- }
假设来到第一次循环,候选数组为:[1,2,3,3,4,5,6]
我们来到第一次选择,这个时候可以选的数字有1,2,3,3,4,5,6
注意在这一轮中,选择第一个3和第二个3,实际上是一样的,所以这里需要去重
每一次调用回溯方法backtrack(),实际上就是进入了一轮选择
在这一轮选择中,我们的起始位置是index
那么在[index + 1 , nums.length - 1]这个区间内,重复的数字都要去掉
就有了下面这一行判断
这一行代表同一轮选择中的情况
if(i > index && candidates[i] == candidates[i-1]) continue;
给一个不含重复数字的整数数组nums,返回其所有可能的全排列
这个题加上一个boolean数组来判断是否选过该数字就可以了,因为数组没有重复数字
- class Solution {
- List<List<Integer>> res;
- public List<List<Integer>> permute(int[] nums) {
- int n = nums.length;
- res = new ArrayList<>();
- boolean[] isVisited = new boolean[n];
- backtrack(nums,isVisited,0,n,new ArrayList<>());
- return res;
- }
-
- public void backtrack(int[] nums,boolean[] isVisited,int count,int n,List<Integer> list){
- if(count == n){
- res.add(new ArrayList<>(list));
- return;
- }
- for(int i = 0; i < nums.length; i++){
- if(!isVisited[i]){
- list.add(nums[i]);
- isVisited[i] = true;
- backtrack(nums,isVisited,count+1,n,list);
- isVisited[i] = false;
- list.remove(list.size() - 1);
- }
- }
- }
- }
和第三题类似,唯一的区别就是数组中存在重复的数字
也是在同一轮次中,选择了相同的数字,就会造成重复
因为我们用到的是boolean数组来判断是否使用过这个数字
那么,之前选择过的数字,isVisited[i]都变成了true
现在没有选择过的数字,isVisited[i]就是false
如果当前数字和前一个数字相同,并且前一个数字并没有被选取,说明他们处在同一轮选择的情况,要跳过
if(i > 0 && nums[i] == nums[i-1] && !isVisited[i-1]) continue;

题目代码:
- class Solution {
- List<List<Integer>> res;
- public List<List<Integer>> permuteUnique(int[] nums) {
- res = new ArrayList<>();
- Arrays.sort(nums);
- backtrack(nums,new boolean[nums.length],0,new ArrayList<>());
- return res;
- }
-
- public void backtrack(int[] nums,boolean[] isVisited,int count,List<Integer> list){
- if(count == nums.length){
- res.add(new ArrayList<>(list));
- return;
- }
-
- for(int i = 0; i < nums.length; i++){
- if(i > 0 && nums[i] == nums[i-1] && !isVisited[i-1]) continue;
- if(!isVisited[i]){
- list.add(nums[i]);
- isVisited[i] = true;
- backtrack(nums,isVisited,count+1,list);
- isVisited[i] = false;
- list.remove(list.size() - 1);
- }
- }
- }
- }
同一轮次中出现重复数字的情况就应该去重
对于总和组合和排列组合来说,同一轮次的情况是不同的
总和组合的同一轮次,是[index , nums.length - 1]这个区间上
而排列组合,同一轮次是isVisited[i]为false的数字