leetcode39.组合总和——学习笔记

题目:力扣leetcode39.组合总和——学习笔记https://leetcode-cn.com/problems/combination-sum/

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> ans = new ArrayList<List<Integer>>();
        Deque<Integer> path = new ArrayDeque<Integer>();
        backTrack(candidates,target,path,ans);
        //set去重
        List<List<Integer>> newAns = new ArrayList<List<Integer>>();
        HashSet set = new HashSet(ans);
        newAns.addAll(set);
        return newAns;
    }

    private void backTrack(int[] candidates,int target,Deque<Integer> path,List<List<Integer>> ans){
        int i = 0;
        //出口
        if(target == 0){
            List<Integer> unit = new ArrayList<Integer>(path);
            Collections.sort(unit);
            ans.add(unit);
            return;
        }else if(target<0 || i==candidates.length){
            return;
        }

        for(i=0;i<candidates.length;i++){
            path.addLast(candidates[i]);
            backTrack(candidates,target-candidates[i],path,ans);
            path.removeLast();
        }
    }
}

leetcode39.组合总和——学习笔记

思路:这题仍然是回溯的思想。因为时间和精力有限,这里只是最暴力的回溯没有进行剪枝的优化,去重的方法是用HashSet去重(相比于在求解时就排除重复解  时间复杂度和空间复杂度更大),因此在用时和内存消耗上都比较大。大概思路就是把target视为树的根,树的叶就是candidates[]数组的各个元素,将target-candidates[i]作为新的target,以此类推递归下去。当target为0时,即找到了解,将所有元素放入一个List中并排好序(这是为了方便后续去重)返回。最后在主方法中调用backTrack即可,然后过一遍HashSet到达去重的效果(HashSet元素不可重复,天然有去重的作用)。最后用一个新的List接收已经“洗”干净的数据,返回结果。

1.这个就是递归的方法(实在不会起名,就backTrack算了...)。需要将候选数组candidates[]、目标值target、用于存储路径的容器path、用于返回列表ans作为入参传进去。

    private void backTrack(int[] candidates,int target,Deque<Integer> path,List<List<Integer>> ans){
        //......
    }

2.定义递归出口。因为在出口判断的时候,需要用到i,所以不得已只能把这个i在前面先声明了。当target为0的时候,意味着已经找打了一个解,我们需要把这条路径记录下来排序后返回,排序是为了方便后续去重操作。如果target值小于0,以为着“这条路”不是解,return回到上一层即可。同样,i为candidates.length时,表明候选项已经用完了,直接返回上一层即可。

int i = 0;
//出口
if(target == 0){
    List<Integer> unit = new ArrayList<Integer>(path);
    Collections.sort(unit);
    ans.add(unit);
    return;
}else if(target<0 || i==candidates.length){
    return;
}

3.每一层都需要遍历一次这个candidates[]数组,并且用path记录下路径,然后递归调用backTrack()进入下一层,记得在回溯的时候要把path的元素也逐一去掉才能得到路径。

for(i=0;i<candidates.length;i++){
    path.addLast(candidates[i]);
    backTrack(candidates,target-candidates[i],path,ans);
    path.removeLast();
}

4.在主方法中声明好需要的变量,然后将这些变量作为入参调用backTrack()方法。ans是用于存储答案结果的,path是用于记录路径的。

List<List<Integer>> ans = new ArrayList<List<Integer>>();
Deque<Integer> path = new ArrayDeque<Integer>();
backTrack(candidates,target,path,ans);

5.HashSet去重。来到这里已经的处理答案,因为在递归方法中是每一层都从头遍历并没有剪枝操作就会导致以下情况:

candidates=[2,3,6,7]  target = 7 求解出现以下结果:[2,2,3] [2,3,2] [3,2,2] ......

但其实上述三个解是相同的,通过之前排序的步骤,它们全都变成[2,2,3]。只需要将所有解传入HashSet中,HashSet天然有去重的功能,然后什么都不用做。用一个新的变量newAns接收“洗干净”的数据。最后返回。

//set去重
List<List<Integer>> newAns = new ArrayList<List<Integer>>();
HashSet set = new HashSet(ans);
newAns.addAll(set);
return newAns;

上一篇:递归与回溯6:LeetCode39组合总和(可重复使用)


下一篇:leetcode40. 组合总和 II