当前位置: 首页 > article >正文

【Leetcode 热题 100】39. 组合总和

问题背景

给你一个 无重复元素 的整数数组 c a n d i d a t e s candidates candidates 和一个目标整数 t a r g e t target target,找出 c a n d i d a t e s candidates candidates 中可以使数字和为目标数 t a r g e t target target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
c a n d i d a t e s candidates candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 t a r g e t target target 的不同组合数少于 150 150 150 个。

数据约束

  • 1 ≤ c a n d i d a t e s . l e n g t h ≤ 30 1 \le candidates.length \le 30 1candidates.length30
  • 2 ≤ c a n d i d a t e s [ i ] ≤ 40 2 \le candidates[i] \le 40 2candidates[i]40
  • c a n d i d a t e s candidates candidates 的所有元素 互不相同
  • 1 ≤ t a r g e t ≤ 40 1 \le target \le 40 1target40

解题过程

题目叫组合总和,实际上是一个子集型的问题,有一点特殊是可以从原集合中取重复值。
由于结果的长度是不确定的,路径可以用列表来维护,递归的边界是找到正好相等的结果或者当前和大于目标和。
有一个剪枝的策略,对原数组进行排序,如果当前 t a r g e t target target(实际上是已经去掉了之前轮次中所选数字之和的目标)小于下一个候选值 c a n d i d a t e [ i ] candidate[i] candidate[i],那么这种情境下后续的所有选择都不可能成为答案了(排序后后面的后选值只会更大)。

这题还可以通过完全背包的视角,进行进一步预处理。
不过这部分基础知识学得并不是很好,暂时不要求掌握。

能应用排序是因为回溯是一种暴力解,回溯本身大概率会比排序耗时。

具体实现

选或不选

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 对候选数组进行排序,方便后面剪枝
        Arrays.sort(candidates);
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(0, target, candidates, path, res);
        return res;
    }
    
    private void dfs(int i, int target, int[] candidates, List<Integer> path, List<List<Integer>> res) {
        if(target == 0) {
            res.add(new ArrayList(path));
            return;
        }
        // 如果枚举到了数组越界位置或者剩余目标小于当前后选址,那么就不必往下递归了
        if(i == candidates.length || target < candidates[i]) {
            return;
        }
        // 不选当前值,那么递归到 i +  1,目标和 target 不变
        dfs(i + 1, target, candidates, path, res);
        // 选择当前值,加入到路径中,target 减少为 target - candidate[i]
        // 由于可以重复选择元素,递归的索引不变
        path.add(candidates[i]);
        dfs(i, target - candidates[i], candidates, path, res);
        // 注意恢复现场
        path.remove(path.size() - 1);
    }
}

选哪一个

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 对候选数组进行排序,方便后面剪枝
        Arrays.sort(candidates);
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(0, target, candidates, path, res);
        return res;
    }
    
    private void dfs(int i, int target, int[] candidates, List<Integer> path, List<List<Integer>> res) {
        if(target == 0) {
            res.add(new ArrayList(path));
            return;
        }
        // 如果枚举到了数组越界位置或者剩余目标小于当前后选址,那么就不必往下递归了
        if(target < candidates[i]) {
            return;
        }
        // j 从 i 开始枚举,不走回头路
        for(int j = i; j < candidates.length; j++) {
            // 尝试选择当前元素
            path.add(candidates[j]);
            dfs(j, target - candidates[j], candidates, path, res);
            // 恢复现场
            path.remove(path.size() - 1);
        }
    }
}

http://www.kler.cn/a/460144.html

相关文章:

  • 神经网络入门实战:(二十三)使用本地数据集进行训练和验证
  • 前端编码技巧与规范
  • Vue3 + ElementPlus动态合并数据相同的单元格(超级详细版)
  • 中小企业如何进行数字化转型?
  • python数据分析:使用pandas库读取和编辑Excel表
  • 浅谈下Spring MVC的执行流程
  • Excel 面试 04 查找函数 XLOOKUP
  • leetcode------mysql
  • 【Lua】元表与元方法
  • 【论文阅读笔记】IceNet算法与代码 | 低照度图像增强 | IEEE | 2021.12.25
  • 我是用git pull每次都要输入账号密码
  • 数据安全技巧:使用私钥认证结合内网穿透实现安全高效的服务器管理
  • 应用层2——FTP文件传输协议
  • QT作业4
  • 一文大白话讲清楚CSS元素的水平居中和垂直居中
  • 浅显易懂的 git 入门
  • 电池放电仪在各领域的作用
  • android——屏幕适配
  • 【Flink运行时架构】系统构架
  • 咚次游戏加速1.1.4.2 | 免费PC游戏加速器,支持1473款游戏加速
  • 如何初始化css样式?为什么要初始化css?
  • python-LeetCode-两数之和
  • Spring Boot缓存预热实战指南
  • .net core 的字符串处理
  • 三大行业案例:AI大模型+Agent实践全景
  • 美畅物联丨视频上云网关获取视频流地址供第三方调用的方法