39 组合总数 (两种方法) (回溯 = 递归 + 回溯)(创建工作数组的副本然后对副本进行操作) Leetcode 57

 class Solution {

        // 回溯类问题 : 递归 + 回溯    (一趟递归结束了,递归结束才是重头戏,现在要向上回溯,回溯还没结束)
        // 对于有多个解的问题,一条路上的递归结束了,找到了一个解( 最好的方法就是在一趟递归结束以后,创建工作数组的副本,任何操作都对这个副本进行,不要动工作数组本身)。这个时候也不能对工作数组做任何一点改动,即使是排序也不行
        // 因为这会使 工作数组内的元素顺序发生改变,从而 “回溯” 的过程中,撤销对结点的操作时发生错乱

        Set<List<Integer>> res;
        LinkedList<Integer> tem;

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

            res = new HashSet<>();
            tem = new LinkedList<>();
            int len = candidates.length;
            dfs(candidates, target, tem);
            return new ArrayList<>(res);

        }


        public void dfs(int[] candidates, int target, LinkedList<Integer> tem) {
            if (target == 0) {
                ArrayList<Integer> tt = new ArrayList<>(tem);
                Collections.sort(tt);
                res.add(tt);
                return;

                // 不能用下面的做法,因为对于有多个结果的回溯问题,即使是一条路上的递归完成了,因为回溯还没完成。
                // 本题错误的地方: 是递归 结束了 ,回溯还没结束, 我就把工作数组给排序了,这样就改变了工作数组内元素的顺序,这样在逐步向上回溯的过程中就会发生错乱
                //           System.out.println("排序前tem ="+tem);
//            Collections.sort(tem);
//            System.out.println("排序后tem ="+tem);
//            res.add(new ArrayList<>(tem));
//            return;

            }

            if (target < 0) {             // 剪枝,不可能产生正确结果的路就直接返回,不用走到路的尽头再返回
                return;
            }

            for (int i = 0; i < candidates.length; i++) {
                if (target >= 0) {
                    tem.add(candidates[i]);
                    dfs(candidates, target - candidates[i], tem);
                    tem.removeLast();
                }
            }

            return;

        }

    }

第二种方法:

利用 begin 在 for循环 中去重,重点体会begin的去重作用:

代码如下;(begin使得当前结点可以重复选取,并且不会回头往前面选取,所有元素要想被多次选取,就只有一次机会,错过了就没了)

加入begin之前:  [2,3,2] 和 [2,2,3] 是重复的答案,必须删除【2,3,2】

 

加入begin之后:成功去重

代码如下:

class Solution {

    Set<List<Integer>> res;
    LinkedList<Integer> tem;

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

        res = new HashSet<>();
        tem = new LinkedList<>();
        int len = candidates.length;
        dfs(candidates, target,0, tem); //begin初始为0
        return new ArrayList<>(res);

    }


    public void dfs(int[] candidates, int target, int begin,LinkedList<Integer> tem) {
        if (target == 0) {
            ArrayList<Integer> tt = new ArrayList<>(tem);
            //Collections.sort(tt);
            res.add(tt);
            return;
        }

        if (target < 0) {       // 剪枝,不可能产生正确结果的路就直接返回,不用走到路的尽头再返回
            return;
        }

        for (int i = begin; i < candidates.length; i++) {
            if (target >= 0) {
                tem.add(candidates[i]);
                dfs(candidates, target - candidates[i], i,tem); // begin为i
                tem.removeLast();
            }
        }

       // return;  不需要

    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雄狮少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值