组合总和 I(力扣第39题)

题目:

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。 

示例:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
  [7],
  [2,2,3]
]

分析:

  给定的数组中的元素是不重复的,但是数组中的元素在同一条搜索路径中是可以无限制选取的,只要能够凑成target这个目标数。这也就意味着,当我们在搜索的时候,从当前的点往下进行搜索的时候,可以选择的范围是数组中所有的元素。下面我们通过一个具体的例子分析搜索结果的整个过程,数组[2,3,5],target=8,其结果搜索过程如下图:

 

  在上面的图中,从根节点到红色叶子结点的路径是能够凑成最终结果的路径,对应分支上的值就是组合成target的元素值,但是我们发现这些路径中的元素组合存在重复的情况,这是我们不想看到的,所以需要进行去重处理,那么应该怎么去重呢?先统计一下这些路径:

  2-2-2-2

  2-3-3

  3-2-3

  3-3-2

  3-5

  5-3

 我们发现第二组合和第三个组合是重复的,重复的原因在于这些元素在搜索路径中的顺序不一样,所以就产生了一条新的路径,也能凑成target,那么应该怎么办呢?很简单,就让搜索路径中,位于前面的分支的值要小于等于后面分支的值,这就能够将重复的组合去掉,从图中观察也正是这样的规律:重复的组合产生的原因就是,由于每次搜索的可选范围都是数组中的所有元素,所以就出现了前面分支中的元素值大于后面元素值的情况,这就可能会造成重复。我们可以以上一次搜索的元素值作为下一次搜索的遍历起始值,来达到这样的效果。同时要时刻判断当前搜索元素值是否大于target,如果大于直接跳过,节省时间。

代码实现:

 private List<List<Integer>> resList;

    public List<List<Integer>> combinationSum(int[] candidates, int target) {

        resList = new ArrayList<>();
        List<Integer> curRes = new ArrayList<>();
        
        findCombinationByDFS(candidates,target,curRes,0);
        return resList;
    }

    private void findCombinationByDFS(int[] candidates, int target, List<Integer> curRes,int start) {
        
        if (target == 0){
            resList.add(new ArrayList<>(curRes));
            return;
        }

        for (int i = start; i < candidates.length; i++) {
            
            if (candidates[i] <= target){
                
                curRes.add(candidates[i]);
                findCombinationByDFS(candidates,target-candidates[i],curRes,i);
                curRes.remove(curRes.size()-1);
            }
        }
        
    }

 

posted @ 2020-07-09 22:58  有心有梦  阅读(166)  评论(0编辑  收藏  举报