LeetCode 39. Combination Sum【回溯,剪枝】中等

news2024/11/16 12:08:19

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。



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

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

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

示例 1:

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

示例 2:

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

示例 3:

输入: candidates = `[2],` target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

解法 搜索回溯

对于这类寻找所有可行解的题,我们都可以尝试用「搜索回溯」的方法来解决。

回到本题,我们定义递归函数 d f s ( t a r g e t , c o m b i n e , i d x ) dfs(target, combine, idx) dfs(target,combine,idx) 表示当前在 c a n d i d a t e s candidates candidates 数组的第 i d x idx idx 位,还剩 t a r g e t target target 要组合,已经组合的列表为 c o m b i n e combine combine 。递归的终止条件为 t a r g e t ≤ 0 target\le 0 target0 或者 c a n d i d a t e s candidates candidates 数组被全部用完。

那么在当前的函数中,每次我们可以选择跳过不用第 i d x idx idx 个数,即执行
d f s ( t a r g e t , c o m b i n e , i d x + 1 ) dfs(target,combine,idx+1) dfs(target,combine,idx+1) 。也可以选择使用第 i d x idx idx 个数,即执行 d f s ( t a r g e t − c a n d i d a t e s [ i d x ] , c o m b i n e , i d x ) dfs(target−candidates[idx],combine,idx) dfs(targetcandidates[idx],combine,idx) ,注意到**每个数字可以被无限制重复选取,因此搜索的下标仍为 i d x idx idx **。

更形象化地说,如果我们将整个搜索过程用一个树来表达,即如下图呈现,每次的搜索都会延伸出两个分叉,直到递归的终止条件,这样我们就能不重复且不遗漏地找到所有可行解:

当然,搜索回溯的过程一定存在一些优秀的剪枝方法来使得程序运行得更快,而这里只给出了最朴素不含剪枝的写法

class Solution {
public:
    vector<vector<int>> ans;
    void dfs(vector<int>& candidates, int target,  vector<int>& combine, int idx) {
        if (idx == candidates.size()) return;
        if (target == 0) {
            ans.emplace_back(combine);
            return;
        }
        // 直接跳过
        dfs(candidates, target, combine, idx + 1);
        // 选择当前数
        if (target - candidates[idx] >= 0) {
            combine.emplace_back(candidates[idx]);
            dfs(candidates, target - candidates[idx], combine, idx);
            combine.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> combine;
        dfs(candidates, target, combine, 0);
        return ans;
    }
};

还可以进行排序,然后剪枝:

class Solution {
public:
    vector<vector<int>> ans;
    void dfs(vector<int>& candidates, int target,  vector<int>& combine, int idx) { 
        if (idx == candidates.size()) return;
        if (target == 0) {
            ans.emplace_back(combine);
            return;
        }
        if (target - candidates[idx] < 0) return;
        // 直接跳过
        dfs(candidates, target, combine, idx + 1);
        // 选择当前数
        combine.emplace_back(candidates[idx]);
        dfs(candidates, target - candidates[idx], combine, idx);
        combine.pop_back();
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> combine;
        sort(candidates.begin(), candidates.end());
        dfs(candidates, target, combine, 0);
        return ans;
    }
};

这种不在意组合内元素顺序的,比如 [2,2,3][3,2,2] 都是答案的,反而要按照特定的某种顺序进行搜索,保证一个答案只会搜索出特定的一种顺序。
在意排列或组合内元素顺序的,往往只需要用哈希表来记录一个元素之前是否使用过。

复杂度分析:

  • 时间复杂度: O ( S ) O(S) O(S) ,其中 S S S 为所有可行解的长度之和。从分析给出的搜索树,我们可以看出时间复杂度取决于搜索树所有叶子节点的深度之和,即所有可行解的长度之和。在这题中,我们很难给出一个比较紧的上界,我们知道 O ( n × 2 n ) O(n \times 2^n) O(n×2n) 是一个比较松的上界,即在这份代码中, n n n 个位置每次考虑选或者不选,如果符合条件,就加入答案的时间代价。但实际运行的时候,因为不可能所有的解都满足条件,递归时我们还会用 t a r g e t − c a n d i d a t e s [ i d x ] ≥ 0 target−candidates[idx]≥0 targetcandidates[idx]0 进行剪枝,所以实际运行情况是远远小于这个上界的。
  • 空间复杂度: O ( t a r g e t ) O(target) O(target) 。除答案数组外,空间复杂度取决于递归的栈深度,在最差情况下需要递归 O ( t a r g e t ) O(target) O(target) 层。

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

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

相关文章

C#小知识

项目编译后复制文件到生成目录 方法1 对于单个文件&#xff0c;可以点击属性。输出目录里选择始终复制。 方法2 把项目中的ServerScripts复制到输出目录。 在项目设置中&#xff0c;生成事件里添加批处理 xcopy $(ProjectDir)ServerScripts\*.* $(TargetDir)ServerScrip…

cms之帝国cms安装

内容摘要 帝国网站管理系统&#xff0c;英文名称为EmpireCMS&#xff0c;简称“帝国CMS”&#xff0c;本文将介绍帝国网站管理系统的安装方法。 前言&#xff1a; 本文安装教程是以帝国CMS7.5版本为基础进行图文讲解。 各位看官&#xff0c;一定要按照每个步骤去执行&#xf…

人机中的事实与价值时空、排序

人机结合智能与事实价值融合分析确实是未来解决复杂疑难问题的基本策略之一。该策略利用人类智慧和机器智能相结合&#xff0c;结合有效的事实和价值分析方法&#xff0c;以更全面、准确、高效地解决问题。 通过人机结合&#xff0c;可以充分发挥人类的主观能动性、判断力和创造…

【Java 基础篇】Java 字节流详解:从入门到精通

Java中的字节流是处理二进制数据的关键工具之一。无论是文件操作、网络通信还是数据处理&#xff0c;字节流都发挥着重要作用。本文将从基础概念开始&#xff0c;深入探讨Java字节流的使用&#xff0c;旨在帮助初学者理解和掌握这一重要主题。 什么是字节流&#xff1f; 在Ja…

【JavaSE笔记】类和对象(万字详解)

一、前言 Java是一种广泛应用于各个领域的编程语言&#xff0c;它的面向对象编程范式使得它成为了当今软件开发的主要选择之一。通过面向对象编程&#xff0c;Java使程序员能够将代码组织成易于理解和维护的结构&#xff0c;并且在开发大型复杂的应用程序时提供了许多便利。 …

STM32单片机——DMA数据传输

STM32单片机——DMA数据传输 DMA相关概述实验一&#xff1a;内存到内存搬运Cubemx工程配置Hal库程序设计及实现固件库程序设计及实现 实验二&#xff1a;内存到外设&#xff08;DMA串口发送&#xff09;Cubemx工程配置Hal库程序设计及实现固件库程序设计 实验三&#xff1a;外设…

FPGA-结合协议时序实现UART收发器(六):仿真模块SIM_uart_drive_TB

FPGA-结合协议时序实现UART收发器&#xff08;六&#xff09;&#xff1a;仿真模块SIM_uart_drive_TB 仿真模块SIM_uart_drive_TB&#xff0c;仿真实现。 vivado联合modelsim进行仿真。 文章目录 FPGA-结合协议时序实现UART收发器&#xff08;六&#xff09;&#xff1a;仿真模…

2023-09-17 LeetCode每日一题(打家劫舍 II)

2023-09-17每日一题 一、题目编号 213. 打家劫舍 II二、题目链接 点击跳转到题目位置 三、题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 &#xff0c;这意味着第一个房屋和最后一个房…

C#,《小白学程序》第二十六课:大数乘法(BigInteger Multiply)的Toom-Cook 3算法及源程序

凑数的&#xff0c;仅供参考。 1 文本格式 /// <summary> /// 《小白学程序》第二十六课&#xff1a;大数&#xff08;BigInteger&#xff09;的Toom-Cook 3乘法 /// Toom-Cook 3-Way Multiplication /// </summary> /// <param name"a"></par…

如何隐藏windows10系统任务栏右下角的语言输入法图标?

勾选“使用桌面语言栏(如果可用)”&#xff0c;任务栏的输入法图标立刻消失

【Java基础夯实】我消化后的ThreadLocal是怎样的?

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;CSDN实力新星&#xff0c;后端开发两年经验&#xff0c;曾担任甲方技术代表&#xff0c;业余独自创办智源恩创网络科技工作室。会点点Java相关技术栈、帆软报表、低代码平台快速开…

面试题:jwt 是什么?java-jwt 呢?

文章目录 JWT概念JWT流程&#xff1a;JWT的构成JWT与开发语言JWT官网java-jwt产生加密Token解密Token获取负载信息并验证Token是否有效 JWT概念 JWT &#xff0c; 全写JSON Web Token, 是开放的行业标准RFC7591&#xff0c;用来实现端到端安全验证. 简单来说&#xff0c; 就是…

OpenCV Series : Target Box Outline Border

角点 P1 [0] (255, 000, 000) P2 [1] (000, 255, 000) P3 [2] (000, 000, 255) P4 [3] (000, 000, 000)垂直矩形框 rect cv2.minAreaRect(cnt)targetColor roi_colortargetThickness 1targetColor (255, 255, 255)if lineVerbose:if …

【斗破年番】紫研新形象,萧炎终成翻海印,救援月媚,三宗决战

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析斗破年番。 斗破苍穹年番动画更新了&#xff0c;小医仙帅气回归&#xff0c;萧炎紫妍成功进入山谷闭关苦修&#xff0c;美杜莎女王守护没多久&#xff0c;就因蛇人族求救离开。从官方公布的最新预告来看&#xff0c;萧炎紫…

地图结构 | 详解八叉树Octomap原理与Rviz可视化

目录 0 专栏介绍1 点云地图的局限性2 八叉树基本原理3 Octovis可视化4 点云转化octomap5 ROS Rviz可视化 0 专栏介绍 &#x1f525;附C/Python/Matlab全套代码&#x1f525;课程设计、毕业设计、创新竞赛必备&#xff01;详细介绍全局规划(图搜索、采样法、智能算法等)&#x…

Web 器学习笔记(基础)

Filter 过滤器 概念&#xff1a;表示过滤器&#xff0c;是 JavaWeb 三大组件&#xff08;Servlet、Filter、Listener&#xff09;之一 作用&#xff1a;顾名思义可以过滤资源的请求&#xff0c;并实现特殊的需求 Filter 接口及它核心的 doFilter() 方法&#xff08;执行前就是…

JVM 第一章:Java运行时数据区

目录 一.了解JVM 1.1什么是JVM 1.2JRE/JDK/JVM 1.3JVM的整体结构 二.Java运行时数据区 2.1程序计数器(PC寄存器) 2.2Java虚拟机栈&#xff08;Java Virtual Machine Stacks&#xff09; 2.2.1栈帧的组成 2.2.2问题辨析 2.2.3逃逸分析 ①栈上分配 ②标量分析 ③同步…

深入探究Spring自动配置原理及SPI机制:实现灵活的插件化开发

文章目录 前言SpringBootApplication 注解AutoConfigurationPackage 注解AutoConfigurationImportSelector SPI 机制和 SpringFactoriesLoaderJDK 中的 SPI 机制SpringFactoriesLoader ConditionalOn 系列条件注解ConditionalOn 系列条件注解的示例ConditionalOn 系列条件注解的…

MySQL中如何识别低效的索引

我是一个目录 前言(可以跳过直接看正文)索引的基本原理索引设计的原则创建索引的原则 正文使用索引查询一定能提高查询的性能吗&#xff1f;怎样查看索引是否有高选择性&#xff1f;用一条SQL查看低效的索引 前言(可以跳过直接看正文) 索引的基本原理 索引用来快速地寻找那些…

学习分布式第一天(分布式系统原理和概念)

目录 分布式系统原理和概念 1.分布式系统&#xff1a; 单体架构&#xff1a; 垂直架构&#xff1a; 分布式架构&#xff1a; 2.分布式计算&#xff1a; 3.CAP 原理&#xff1a; 4.BASE 理论&#xff1a; 5.Paxos 算法&#xff1a; 6.Raft 算法&#xff1a; 分布式系统原…