LeetCode组合总和

组合总和

前言

在上篇文章通过组合问题看透回溯法当中我们通过介绍一个组合问题,仔细地分析了组合问题的回溯过程。我们之后会继续介绍一些比较经典的回溯算法题,帮助深入彻底理解回溯算法的执行过程和原理,如果对回溯的整个过程还不是很了解的话可以先阅读上面那篇文章。

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

例1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

例2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

解法

根据题目的意思我们是需要从给定的集合当中选出几个数据,而且可以进行重复选取,只需要保证被我们选中的数据的和为 target即可。对于这个问题我们可以构造一个如下图所示的解树:(下面的树的 target = 8, candidates = [2,3,5],当被选中的数据的和大于8的时候我们不需要再往下进行遍历了,因为再选择数据的话,被选择的数据的和只会大于8,这永远不可能等于 target = 8

LeetCode组合总和

可能你会对上面的解树有所疑问,为什么我们选完2之后是从 [2, 3, 5]当中继续选择数据,选择完3之后是从 [3,5]当中继续选择数据,而选择完5之后只能从 [5]当中选择数据呢?

事实上我们是需要穷举所有的组合的然后一一进行匹配,所以我们现在的问题是上面的树穷举完所有的组合了吗?上面的树是穷举完所有的组合了的,下面我们分析一下为什么上面的树能够覆盖所有的组合。

穷举的组合如下:

  • 第一个数据和其他所有数据进行组合,
  • 第二个数据和其他所有数据的组合。
  • 第三个数据和其他所有数据的组合。
  • ….

我们现在来仔细分析一样上面的解树:

  • 我们再进行第一次选择的时候,当我们选择完2之后还可以从 [2, 3, 5]当中进行选择,这就表示了2和其他所有数据 [3, 5]进行了组合,因为题目说明我们还可以进行重复选择,因此还加入了2,这就保证了2进行了所有的组合。
  • 我们再进行第一次选择的时候。当我们选择3的时候,还能从 [3, 5]当中进行选择,选择3的原因也是因为可以重复进行选择,但是选择5的原因是因为需要和其他所有数据进行组合。在这里你可能还会有一个疑问就是3不是只和5进行了组合吗,还缺它和2的组合啊,仔细想一想我们在前一步选择2的时候已经将2和3的组合遍历过一遍了,因此在这不需要再进行组合了。
  • 同样的道理我们在第一次选择5的时候,不需要和在他前面的数据进行组合,因为在他前面的数据已经遍历过所有跟5的组合了。

LeetCode组合总和

写代码的流程

  • 仔细分析程序递归的过程,分析求解问题的树,得到代码的主题思路,根据分析的结果我们可以设计我们函数会有哪些参数,在这道题目当中我们需要记录选中的数据的集合的和,这样就可以避免反复去计算选中数据的和了,还有当前遍历到数据的下标。
backtrace(vector &candidates, int target, int idx, int curSum)
  • 确定递归函数的出口:
  • 在这里我们一个出口就是,当被选中的集合当中的数据的和满足要求的时候就进行退出。
  • 当被选中的数据的和大于给定的值的时候我们不需要再进行遍历了,因为即使遍历得到的结果使用会大于给定的值,因此没有遍历的必要了,下图当中深红色的节点就是表示和大于给定的值的情况。

LeetCode组合总和
if(curSum == target) {
    ans.push_back(path);
}
if(curSum >= target || idx >= candidates.size())
    return;
  • 根据我们思路确定我们函数的主体部分:
for (int i = idx; i < candidates.size(); ++i) {
    path.push_back(candidates[i]);
    curSum += candidates[i];
    backtrace(candidates, target, i, curSum);
    path.pop_back();
    curSum -= candidates[i];
}

完整代码

C++版

class Solution {

    vector> ans;
    vector path;
public:
    vector> combinationSum(vector& candidates, int target) {
        backtrace(candidates, target, 0, 0);
        return ans;
    }

    void backtrace(vector &candidates, int target, int idx, int curSum) {
        if(curSum == target) {
            ans.push_back(path);
        }
        if(curSum >= target || idx >= candidates.size())
            return;
        for (int i = idx; i < candidates.size(); ++i) {
            path.push_back(candidates[i]);
            curSum += candidates[i];
            backtrace(candidates, target, i, curSum);
            path.pop_back();
            curSum -= candidates[i];
        }
    }
};

Java

class Solution {
    private List> res = new ArrayList<>();
    private List path = new ArrayList<>();

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

        solve(candidates, target, 0, 0);
        return res;
    }

    public void solve(int[] candidates, int target, int startPosition,
                      int curSum) {
        if (curSum == target) {
            res.add(new ArrayList<>(path));
            return;
        }
        else if (curSum > target) return;
        for (int i = startPosition; i < candidates.length; i++) {
            path.add(candidates[i]);
            curSum += candidates[i];
            solve(candidates, target, i, curSum);
            path.remove(path.size() - 1);
            curSum -= candidates[i];
        }
    }

}

总结

这道题目体现了经典的回溯过程,就是在往集合当加入数据之后,再经过遍历完成,就需要将之前加入的数据移出,而这个回到开始的状态的现象就叫做回溯,即回到之前的状态。但是在这个题目当中还需要注意一点的是他的数据可以重复进行使用,因此在遍历传递参数的时候需要传递的是 i而不是 i+1,这一点需要注意。

以上就是本篇文章的所有内容了,我是 LeHung,我们下期再见!!!更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore

关注公众号: 一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。

Original: https://www.cnblogs.com/Chang-LeHung/p/16716324.html
Author: 一无是处的研究僧
Title: LeetCode组合总和

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/586906/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • rabbitmq报错:not_a_dets_file,”/var/lib/rabbitmq/mnesia/rabbit@Sfabrici-Demo01/recovery.dets”的解决办法

    1,故障现象,rabbitmq无法启动,报错截图如下: 2,解决办法:删除掉这个文件 (base) root@Sfabrici-Demo01:/usr/lib/rabbitmq/b…

    Java 2023年5月30日
    074
  • java垃圾收集器与内存分配策略(2)

    1.概述 说起垃圾收集(Garbage Collection, GC),大部分人都把这项技术当做Java语言的伴生产物。事实上,GC的历史远远比Java久远,1960年诞生于MIT…

    Java 2023年6月13日
    061
  • HashMap源码分析

    主要过一遍HashMap中的常量、构造方法、put方法(hash、putVal、resize) 当我们调用put时,实际上就是调用putVal public V put(K key…

    Java 2023年6月15日
    076
  • SpringCloud是什么?

    参考链接: http://blog.csdn.net/forezp/article/details/70148833 一、概念定义 Spring Cloud是一个微服务框架,相比D…

    Java 2023年5月30日
    068
  • git 重写历史

    link:date: 2022-08-30 历史提交commit信息修改 修改最新log $ git commit –amend 修改多个提交信息 Git 没有一个改变历史工具,…

    Java 2023年6月13日
    070
  • Java性能调优工具

    JDK命令行:jps、jinfo、jstat、jmapMAT:Eclipse Memory AnalyzerJMX – Jconsole,VisualVMBtrace:…

    Java 2023年5月29日
    061
  • 全链路spring cloud sleuth+zipkin

    一、About ZipKin please google 二、 Demo Scene 三、 Result Display 四、Prepare 1、soft version kafk…

    Java 2023年5月30日
    0100
  • @Autowired和@Resouce的区【转】

    @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要…

    Java 2023年6月9日
    058
  • SpringBoot 实现 excel 全自由导入导出,性能强的离谱,用起来还特优雅

    一、简介 在实际的业务系统开发过程中,操作 Excel 实现数据的导入导出基本上是个非常常见的需求。 之前,我们有介绍一款非常好用的工具:EasyPoi,有读者提出在数据量大的情况…

    Java 2023年6月9日
    052
  • JS 模块化-02 Common JS 模块化规范

    1 Common JS 介绍 Common JS 是模块化规范之一。每个文件都是一个作用域,文件里面定义的变量/函数都是私有的,对其他模块不可见。Common JS 规范在 Nod…

    Java 2023年6月16日
    075
  • 01-SpringCloud介绍

    Spring Cloud provides tools for developers to quickly build some of the common patterns in…

    Java 2023年6月7日
    086
  • 什么是线上优雅停机和调整线程池参数?

    我是3y,一年 CRUD经验用十年的 markdown程序员👨🏻‍💻常年被誉为职业八股文选手 好几天没更新 austin的系列文章啦,主要是一直在写 austin的代码。而这篇文章…

    Java 2023年6月9日
    092
  • 并发编程之:ForkJoin

    大家好,我是小黑,一个在互联网苟且偷生的农民工。 在JDK1.7中引入了一种新的Fork/Join线程池,它可以将一个大的任务拆分成多个小的任务并行执行并汇总执行结果。Fork/J…

    Java 2023年6月7日
    074
  • 将springboot安装成windows服务启动。

    下载Windows Service Wrapper 本文下载了winsw-2.3.0-bin.exe。 新建一个目录aiplatformService 在目录里面新建一个aipla…

    Java 2023年5月30日
    095
  • java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter

    在构建项目的时候使用的是jdk11,项目访问时报错 故障原因 使用了jdk版本过高 解决:直接在pom.xml中添加如下依赖可以解决(也可以试试降低jdk的版本) javax.xm…

    Java 2023年5月29日
    070
  • .net 反射简单介绍

    1.什么是反射 反射是.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如…

    Java 2023年5月30日
    0128
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球