算法——LeetCode39. 组合总和

39. 组合总和

原题链接

1. 题目:

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

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

说明:

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

示例 1:

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

示例 2:

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

 

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

2. 题解1:回溯法(递归DFS)

主要思路:

  1. 每个递归结点,保存输入数组,目标数,结果集,当前和,当前开始下标,当前列表
  2. 避免出现重复组合,需要加入一个 start 下标索引,在每次进入下一层时,选择的数都是从上一层选择的数开始, 即保证该索引之前的数不再重复选择

代码:
使用差来进行状态保存

    class Solution {
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            List<List<Integer>> ans = new ArrayList<>();
            dfs(target, new ArrayList<>(), 0, candidates, ans);
            return ans;
        }

        public void dfs(int remain, List<Integer> list, int start, int[] candidates, List<List<Integer>> ans) {
            if (remain < 0) return;
            if (remain == 0) {
                ans.add(new ArrayList<>(list));
                return;
            }
            for (int i = start; i < candidates.length; i++) {
                list.add(candidates[i]);
                dfs(remain - candidates[i], list, i, candidates, ans);
                list.remove(list.size() - 1);
            }
        }
    }

写法2:

    class Solution {
        List<List<Integer>> res = new ArrayList<>();
        int[] candidates;
        int target;

        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            this.candidates = candidates;
            this.target = target;
            dfs(0, 0,new ArrayList<>());
            return res;
        }

        /**
         * 递归函数
         * @param sum   当前和
         * @param start 当前开始下标
         * @param list  当前列表
         */
        public void dfs(int sum, int start, List<Integer> list) {
            // 递归终止条件判断, 如果当前结点和已经大于target,结束搜索
            if (sum > target) {
                return;
            }
            if (sum == target) {
                res.add(new ArrayList<>(list));
                return;
            }
            for (int i = start; i < candidates.length; i++) {
                list.add(candidates[i]);
                int tempSum = sum + candidates[i];
                dfs(tempSum, i, list);
                // 消除状态影响
                list.remove(list.size() - 1);
            }
        }
    }

3. 题解2: 优化:排序+剪枝

主要思路:

  • 先对数组进行排序,然后当 下一层结点的 sum>target 时,提前结束for循环
  • 该方法代码只比题解1,多了三四行,基本沿用题解1的代码
  • 速度上可以提升不少,实际测试时题解1用时8ms,此方法用时3ms

代码:

    class Solution {
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            List<List<Integer>> ans = new ArrayList<>();
            Arrays.sort(candidates);
            dfs(target, new ArrayList<>(), 0, candidates, ans);
            return ans;
        }

        public void dfs(int remain, List<Integer> list, int start, int[] candidates, List<List<Integer>> ans) {
            if (remain < 0) return;
            if (remain == 0) {
                ans.add(new ArrayList<>(list));
                return;
            }
            for (int i = start; i < candidates.length; i++) {
                //剪枝判断,由于数组有序,当这一个结点sum>target ,停止for循环
                int tempRemain = remain - candidates[i];
                if (tempRemain < 0) {
                    break;
                }
                list.add(candidates[i]);
                dfs(tempRemain, list, i, candidates, ans);
                list.remove(list.size() - 1);
            }
        }
    }

**写法2: **

    class Solution {
        List<List<Integer>> res = new ArrayList<>();
        int[] candidates;
        int target;

        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            this.candidates = candidates;
            // 对数组进行排序
            Arrays.sort(candidates);
            this.target = target;
            dfs(0, 0, new ArrayList<>());
            return res;
        }

        /**
         * 递归函数
         *
         * @param sum   当前和
         * @param start 当前开始下标
         * @param list  当前列表
         */
        public void dfs(int sum, int start, List<Integer> list) {
            // 递归终止条件判断, 如果当前结点和已经大于target,结束搜索
            if (sum > target) {
                return;
            }
            if (sum == target) {
                res.add(new ArrayList<>(list));
                return;
            }
            for (int i = start; i < candidates.length; i++) {
                //剪枝判断,由于数组有序,当这一个结点sum>target ,停止for循环
                int tempSum = sum + candidates[i];
                if (tempSum > target) {
                    break;
                }
                list.add(candidates[i]);
                dfs(tempSum, i, list);
                // 消除状态影响
                list.remove(list.size() - 1);
            }
        }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值