Leetcode39 组合总和

news2025/1/12 1:06:08

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

 解析:

思路分析:根据示例 1:输入: candidates = [2, 3, 6, 7],target = 7。

候选数组里有 2,如果找到了组合总和为 7 - 2 = 5 的所有组合,再在之前加上 2 ,就是 7 的所有组合;
同理考虑 3,如果找到了组合总和为 7 - 3 = 4 的所有组合,再在之前加上 3 ,就是 7 的所有组合,依次这样找下去。
基于以上的想法,可以画出如下的树形图。建议大家自己在纸上画出这棵树,这一类问题都需要先画出树形图,然后编码实现。

编码通过 深度优先遍历 实现,使用一个列表,在 深度优先遍历 变化的过程中,遍历所有可能的列表并判断当前列表是否符合题目的要求,成为「回溯算法」(个人理解,非形式化定义)。

回溯算法的总结我写在了「力扣」第 46 题(全排列)的题解 《回溯算法入门级详解 + 经典例题列表(持续更新)》 中,如有需要请前往观看。

以输入:candidates = [2, 3, 6, 7]target = 7 为例:

说明:

以 target = 7 为 根结点 ,创建一个分支的时 做减法 ;
每一个箭头表示:从父亲结点的数值减去边上的数值,得到孩子结点的数值。边的值就是题目中给出的 candidate 数组的每个元素的值;
减到 000 或者负数的时候停止,即:结点 000 和负数结点成为叶子结点;
所有从根结点到结点 000 的路径(只能从上往下,没有回路)就是题目要找的一个结果。
这棵树有 444 个叶子结点的值 000,对应的路径列表是 [[2, 2, 3], [2, 3, 2], [3, 2, 2], [7]],而示例中给出的输出只有 [[7], [2, 2, 3]]。即:题目中要求每一个符合要求的解是 不计算顺序 的。下面我们分析为什么会产生重复。

产生重复的原因是:在每一个结点,做减法,展开分支的时候,由于题目中说 每一个元素可以重复使用,我们考虑了 所有的 候选数,因此出现了重复的列表。

一种简单的去重方案是借助哈希表的天然去重的功能,但实际操作一下,就会发现并没有那么容易。

可不可以在搜索的时候就去重呢?答案是可以的。遇到这一类相同元素不计算顺序的问题,我们在搜索的时候就需要 按某种顺序搜索。具体的做法是:每一次搜索的时候设置 下一轮搜索的起点 begin,请看下图。

即:从每一层的第 222 个结点开始,都不能再搜索产生同一层结点已经使用过的 candidate 里的元素。 

 

友情提示:如果题目要求,结果集不计算顺序,此时需要按顺序搜索,才能做到不重不漏。「力扣」第 47 题( 全排列 II )、「力扣」第 15 题( 三数之和 )也使用了类似的思想,使得结果集没有重复。

剪枝提速
根据上面画树形图的经验,如果 target 减去一个数得到负数,那么减去一个更大的树依然是负数,同样搜索不到结果。基于这个想法,我们可以对输入数组进行排序,添加相关逻辑达到进一步剪枝的目的;
排序是为了提高搜索速度,对于解决这个问题来说非必要。但是搜索问题一般复杂度较高,能剪枝就尽量剪枝。实际工作中如果遇到两种方案拿捏不准的情况,都试一下。

 实现代码

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if(candidates.length == 0){
            return res;
        }
        //排序是剪枝的前提
        Arrays.sort(candidates);
        Deque<Integer> path = new ArrayDeque<>();
        dfs(candidates,0,target,path,res);
        return res;
    }

    private void dfs(int[] candidates, int begin, int target, Deque<Integer> path, List<List<Integer>> res){
        if(target == 0){
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = begin; i < candidates.length;i++) {
            //重点理解这里的剪枝,前提是数组已经有序
            if(target - candidates[i] < 0){
                break;
            }
            path.addLast(candidates[i]);
            System.out.println("递归之前 => " + path + ",剩余 = " + (target - candidates[i]));

            //由于每一个元素可以重复使用,下一轮搜索的起点依然是i(顺序搜索去重了)
            //如果不能重复使用的滑,i+1
            //如果不去重的话,i=0
            dfs(candidates, i ,target - candidates[i], path ,res);
            //状态重置
            path.removeLast();
            System.out.println("递归之后 => " + path);
        }

    }
}

总结:

什么时候使用 used 数组,什么时候使用 begin 变量
有些朋友可能会疑惑什么时候使用 used 数组,什么时候使用 begin 变量。这里为大家简单总结一下:

排列问题,讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为不同列表时),需要记录哪些数字已经使用过,此时用 used 数组;
组合问题,不讲究顺序(即 [2, 2, 3] 与 [2, 3, 2] 视为相同列表时),需要按照某种顺序搜索,此时使用 begin 变量。
注意:具体问题应该具体分析, 理解算法的设计思想 是至关重要的,请不要死记硬背。

补充说明

如果对于「回溯算法」的理解还很模糊的朋友,建议在「递归」之前和「递归」之后,把 path 变量的值打印出来看一下,以加深对于程序执行流程的理解。

<1递归之前 => [2],i=0,剩余 = 5
<2递归之前 => [2, 2],i=0,剩余 = 3
<3递归之前 => [2, 2, 2],i=0,剩余 = 1
3>递归之后 => [2, 2],(1-2)<0,break跳出循环dfs执行完,接着执行remove,回溯删除arr[2],递归<3>结束
<4递归之前 => [2, 2, 3],i=1,重新执行for循环,剩余 = 0,res,add()
4>递归之后 => [2, 2] 接着执行remove,回溯删除arr[2],递归<4>结束, 3-6<0,剪枝
2>递归之后 => [2] 接着执行remove,回溯删除arr[1] ,递归<2>结束,(5-6)<0,剪枝
<5递归之前 => [2, 3],剩余 = 2
5>递归之后 => [2] (4-6)<0,剪枝
1>递归之后 => []

-----
<1递归之前 => [3],剩余 = 4
<2递归之前 => [3, 3],剩余 = 1
2>递归之后 => [3]
1>递归之后 => []

------
递归之前 => [6],剩余 = 1
递归之后 => []

--------
递归之前 => [7],剩余 = 0
递归之后 => []
输出 => [[2, 2, 3], [7]]

参考解题链接:

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/876044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EndNote 21 for Mac(文献管理软件) v21.0.1中文版

EndNoter mac是一款参考文献管理软件&#xff0c;旨在帮助学术研究者、学生和专业人士有效地管理和引用参考文献。该软件提供了许多功能&#xff0c;使用户可以轻松地组织、搜索和引用各种类型的文献。 EndNoter mac软件特点和功能 1. 参考文献管理&#xff1a;EndNoter允许用…

各位请看:如何使用ChatGPT让你的小生意做得更好?

​在当前快节奏的商业环境中&#xff0c;有许多小型企业依靠行动效率和创造力正在茁壮成长。OpenAI的ChatGPT这样的AI工具正为小型企业发展作出贡献。 ChatGPT是基于OpenAI语言模型架构GPT-3.5和GPT-4的人工智能聊天机器人。它根据大量的互联网文本数据集预测句子中的下一个单词…

小鹏G9高压电驱动800V拆解【实拍】

目前已知G9基于800V 电池系统和Sic电驱系统进行开发。但这一系统的具体参数、技术水平都尚无官方解析。这里基于欣旺达的sfc480电池发布会ppt资料(图1)&#xff0c;可以看出欣旺达的4c电池满电电压在4.4V&#xff0c;这和taycan 4S的800v系统最大电压基本相同&#xff0c;电池系…

03-基础入门-搭建安全拓展

基础入门-搭建安全拓展 1、涉及的知识点2、常见的问题3、web权限的设置4、演示案例-环境搭建&#xff08;1&#xff09;PHPinfo&#xff08;2&#xff09;wordpress&#xff08;3&#xff09;win7虚拟机上使用iis搭建网站&#xff08;4&#xff09;Windows Server 2003配置WEB站…

Jmeter性能测试系列-性能测试需求分析

性能测试需求分析 性能测试需求分析与传统的功能测试需求有所不同&#xff0c;功能测试需求分析重点在于从用户层面分析被测对象的功能性、易用性等质量特性&#xff0c;性能测试则需要从终端用户应用、系统架构设计、硬件配置等多个纬度分析系统可能存在性能瓶颈的业务。 性…

搭建网站并内网穿透实现公网访问本地SQL Server数据库【无公网IP内网穿透】

文章目录 前言1. 安装网站运行和发布必备软件2. 安装PHPStudy3. 安装wordpress4. 进入wordpress安装程序&#xff0c;进行网页编辑和设置5. 安装URL插件6. 安装Cpolar7. 创建自己的数据隧道 前言 在普通电脑用户看来&#xff0c;建立自己的网站总是一件高大上的事情&#xff0…

【Redis实践篇】使用Redisson 优雅实现项目实践过程中的5种场景

文章目录 1.前言2.使用方式1. 添加Redisson依赖&#xff1a;2. 配置Redis连接信息3. 使用场景3.1. 分布式锁3.2. 限流器&#xff08;Rate Limiter&#xff09;3.3. 可过期的对象&#xff08;Expirable Object&#xff09;3.4. 信号量&#xff08;Semaphore&#xff09;3.5. 分布…

安装docker的报错问题

先关闭安全机制 systemctl stop firewalld.service setenforce 0 安装依赖包 yum install -y yum-utils device-mapper-persistent-data lvm2 设置阿里云镜像云 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 安装d…

python列表中的元素去重

方法&#xff1a;利用set()函数。 list_t1 [ab, 3, 4, 4, a, ab] list_t2 list(set(list_t1))print(list_t2) print(list_t1)结果&#xff1a; 练习&#xff1a;字符串去重后&#xff0c;重新排序。‘3,6,7,7,2’ 变成 ‘2,3,6,7’ # 参数&#xff1a; # str1 字符串&…

bilibili倍数脚本,油猴脚本

一. 内容简介 bilibili倍数脚本&#xff0c;油猴脚本 二. 软件环境 2.1 Tampermonkey 三.主要流程 3.1 创建javascript脚本 点击添加新脚本 就是在 (function() {use strict;// 在这编写自己的脚本 })();倍数脚本&#xff0c;含解析 // UserScript // name bi…

STABLE DIFFUSION模型及插件的存放路径

记录下学习SD的一些心得&#xff0c;使用的是秋叶大佬的集成webui&#xff0c;下载了之后点击启动器即可开启&#xff0c;文件夹中的内容如下 主模型存放在models文件下的stable-diffusion文件夹内&#xff0c;一些扩展类的插件是存放在extensions文件夹下

Django框架 靓号管理(增删改查)

Django框架 靓号管理&#xff08;增删改查&#xff09; 新建一个项目 backend 使用pycharm创建app startapp app项目目录 C:\code\backend ├── app | ├── admin.py | ├── apps.py | ├── migrations | ├── models.py | ├── tests.py | ├── views.…

win11电脑查找已连接打印机ip的方法

此方法适用于驱动打印机&#xff0c;windows 11操作系统。 方法一&#xff1a;直接查看法 首先大家可以看看自己的打印机有没有lcd屏幕。有些直接在屏幕显示ip&#xff1b;另一种进入菜单&#xff0c;然后可以在里面的选项中显示“ip地址”。 方法二&#xff1a;设置中查看 …

Java课题笔记~ SpringMVC概述

1.1 SpringMVC简介 SpringMVC 也叫Spring web mvc。是Spring 框架的一部分&#xff0c;在Spring3.0 后发布的。 1.2 SpringMVC的优点 基于MVC 架构 基于 MVC 架构&#xff0c;功能分工明确。解耦合。 容易理解&#xff0c;上手快&#xff0c;使用简单 就可以开发一个注解…

【数据分享】2001-2022年我国省市县镇四级的逐月最高气温数据(无需转发/Shp/Excel格式)

气象数据是在各项研究中都非常常用的数据&#xff01;之前我们分享过来自于国家青藏高原科学数据中心的1901-2022年1km分辨率的逐月平均气温栅格数据&#xff0c;以及基于该栅格数据处理的Shp和Excel格式的2001-2022年我国省市县镇四级的逐月平均气温数据&#xff08;可查看之前…

执行Lua脚本后一直查询不到Redis中的数据(附带问题详细排查过程,一波三折)

文章目录 执行Lua脚本后一直查询不到Redis中的数据&#xff08;附带详细问题排查过程&#xff0c;一波三折&#xff09;问题背景问题1&#xff1a;Lua脚本无法切库问题2&#xff1a;RedisTemlate切库报错问题3&#xff1a;序列化导致数据不一致问题4&#xff1a;Lua脚本中单引号…

docker安装redis7-单机版

下载redis镜像 redis镜像各版本参考&#xff1a;redis镜像 &#xff0c;这里以redis7.0.5为例 docker pull redis:7.0.5 创建挂载目录 挂载目录按照自己挂载的路径去创建对应的目录即可 mkdir -p /root/data/redis/conf(自己的挂载路径) mkdir -p /root/data/redis/data(自…

如何部署Zabbix监控服务

目录 一、zabbix概念 二、zabbix 监控原理 三、部署Zabbix服务 第一步 关闭防火墙和安全机制 第二步 获取Zabbix下载源 第三步 更换Zabbix阿里源 第四步 安装依赖环境 第五步 安装SCL服务 第六步 修改源配置文件 第七步 安装Zabbix依赖环境 第八步 安装Zabbix所需的…

【springboot启动报错】java: 错误: 无效的源发行版:17

报错截图 解决方案 第一步&#xff1a;编辑配置&#xff0c;改为想用的jdk版本 第二步&#xff1a;文件--->项目结构&#xff0c;改为对应的SDK 第三步&#xff1a;文件--->设置--->构建、执行、部署--->编译器--->Java编译器&#xff0c;修改目标字节码版本 第…

SAP 超级BOM物料语法报表(SAP配置BOM攻略七)

我需要一个报表&#xff0c;输入超级BOM&#xff0c;把其中的物料的语法相关性展现出来&#xff0c;CS12和CS03都没有这个清单功能。网上很多CS_BOM_EXPL_MAT_V2函数的使用了&#xff0c;我也来一篇我的吧。 *&----------------------------------------------------------…