40-组合总和2

题目:

 与上一题的不同:

1.在上一个“组合总和”中,每个元素可选择无数次,这个题里面,一个元素只能选一次。

2. 上一个题里,元素都不重复,这个题,元素可重复。

思路:和上一题差不多,回朔法。先排序。假如给出的数1,2,2,2。target==5;因为每个数只能选一次,所以要有一个数组visited,数值为1,说明这个下标代表的数已经选过了。不能再选。

第一轮,1加入tmp,之后visited[0]=1。递归调用。递归时,虽然start依然为0,但是,visited[0]==1,已经被访问过,所以不会再被访问,2被加入。。。最后,得出包含“1”的结果。别忘了回溯时,要把tmp里加入的元素踢出来,visited也要设置为0,因为在一轮循环里,可能访问过数组里所有的元素,但是下一轮循环,start加一之后,如果没有设置为0的操作,这个数就无法访问了

第二轮循环:1被踢出去,从2开始,2加入tmp,visited[1]=1。递归调用,找出包含2的结果。。。

补充,因为解集里不能包含重复元素,res里加入tmp时,要用res.contains(tmp);判断tmp这个集合,是否已经在res里面。

class Solution {//回溯法
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res =new ArrayList<>();
        Arrays.sort(candidates);
        int[] visited=new int[candidates.length];
        backpack(res,candidates,target,0,new ArrayList<Integer>(),visited);
        return res;
    }
    public void backpack(List<List<Integer>> res,int [] candidates,int target,int i,List<Integer> tmp,int[] visited)//i是搜索起点的下标,因为数字可以重复选取,下一层的结点从这个搜索起点开始搜索;这个起点之前的一定被搜索过了,所以不需要
    {
        if(target==0&&!res.contains(tmp))
        {
            res.add(new ArrayList<>(tmp));
            return;
        }
        if(target<0) return;
        for(int start=i;start<candidates.length;start++)//一轮循环,找的是tmp里加入了candidates[start]的所有组合。下一轮,找的是,tmp里加入candidates[start+1](candidates[start]不加,因为带这个数的所有组合都找到了)的所有组合。
        {
           if(visited[start]==1)continue;//已访问过的不再访问。
            visited[start]=1;
            tmp.add(candidates[start]);
            backpack(res,candidates,target-candidates[start],start,tmp,visited);
            tmp.remove(tmp.size()-1);
            visited[start]=0;
        }
    }
}

  

原文地址:https://www.cnblogs.com/lzh1043060917/p/12767409.html