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

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]]

示例 3:

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

题目分析

基本思想基于深度搜索回溯

首先对候选数组做一个排序,且由于数组都是正数,可以直接进行搜索。

代码

/*** @param {number[]} candidates* @param {number} target* @return {number[][]}*/
var combinationSum = function (candidates, target) {candidates.sort((a, b) => a - b)var item = [],path = [];get_combin(candidates, target, 0, item, path);function get_combin(candidates, target, it, item, path) {if (target < 0)// 如果大于target 直接返回不继续搜索return;if (target == 0) {// 若得到路径,插入到item,不用清空path。因为需要继续搜索其余可能性path = path.slice()  //进行复制,slice()没有参数就是复制item.push(path);return}for (var i = it; i < candidates.length; i++) {path.push(candidates[i]);get_combin(candidates, target - candidates[i], i, item, path)// 无论是该路径大于target还是等于target,都需要对其删除最后一个元素,进行其余支路的搜索path.pop()}}return item
};

代码分析

这段代码通过深度优先搜索(DFS)和回溯算法来找出所有可能的组合,使得这些组合的元素之和等于目标值target。下面是逐行解释:

  1. candidates.sort((a, b) => a - b)
    这行代码对输入数组candidates进行排序。排序是为了使得我们能够按照从小到大的顺序进行搜索,这样可以更高效地找到可能的组合。

  2. var item = [], path = [];
    声明两个数组,item用于存储所有找到的组合,path用于存储当前搜索路径上的元素。

  3. get_combin(candidates, target, 0, item, path);
    调用递归函数get_combin开始搜索。参数分别是候选数组、目标和、当前索引(从0开始)、存储所有组合的数组item和当前路径的数组path

  4. function get_combin(candidates, target, it, item, path) { ... }
    定义递归函数get_combin

  5. if (target < 0) return;
    在递归函数中,如果当前目标和小于0,说明当前路径上的元素之和已经超过了目标值,因此不需要继续搜索这条路径。

  6. if (target == 0) { ... }
    在递归函数中,如果当前目标和等于0,说明找到了一个有效的组合。将当前路径path复制并添加到item数组中。

  7. path = path.slice()
    使用slice方法复制当前路径,因为path是引用类型,如果不复制,那么在递归调用中修改path会影响到上一层递归的路径。

  8. item.push(path);
    将复制的路径添加到结果数组item中。

  9. return
    找到有效组合后返回,不再继续搜索当前路径。

  10. for (var i = it; i < candidates.length; i++) { ... }
    使用for循环遍历候选数组,从当前索引it开始,直到数组末尾。

  11. path.push(candidates[i]);
    将当前元素添加到路径中。

  12. get_combin(candidates, target - candidates[i], i, item, path)
    递归调用get_combin函数,更新目标值为当前目标值减去当前元素的值,索引更新为当前索引i(允许重复使用当前元素),继续搜索。

  13. path.pop()
    回溯步骤,从路径中移除最后一个元素,以便尝试其他可能的元素。

整个算法的关键在于递归和回溯。通过递归深入搜索每一种可能的组合,当找到一个有效组合或者超过目标值时回溯,尝试其他可能的组合。这种方法虽然简单,但效率较高,因为它避免了不必要的搜索。

案例分析

通过一个具体的例子来理解这段代码是如何工作的。我们使用示例 1 的输入:

candidates = [2,3,6,7], target = 7
  1. 排序:首先,candidates 数组被排序为 [2, 3, 6, 7]

  2. 初始化itempath 被初始化为空数组。

  3. 开始递归:调用 get_combin(candidates, 7, 0, item, path)

    • 递归深度 1

      • 目标是 7,当前索引是 0,当前元素是 2。
      • 因为 7 - 2 = 5,大于 0,所以继续递归。
      • path 变为 [2]
      • 递归调用 get_combin([2,3,6,7], 5, 0, item, [2])
    • 递归深度 2

      • 目标是 5,当前索引是 0,当前元素是 2。
      • 因为 5 - 2 = 3,大于 0,所以继续递归。
      • path 变为 [2, 2]
      • 递归调用 get_combin([2,3,6,7], 3, 0, item, [2, 2])
    • 递归深度 3

      • 目标是 3,当前索引是 0,当前元素是 2。
      • 因为 3 - 2 = 1,大于 0,所以继续递归。
      • path 变为 [2, 2, 2]
      • 递归调用 get_combin([2,3,6,7], 1, 0, item, [2, 2, 2])
    • 递归深度 4

      • 目标是 1,当前索引是 0,当前元素是 2。
      • 因为 1 - 2 < 0,所以不继续递归。
      • 回溯到递归深度 3。
    • 递归深度 3 回溯:

      • path 中移除最后一个元素,path 变为 [2, 2]
      • 因为目标是 3,当前索引是 0,但我们已经尝试过当前元素,所以移动到下一个元素。
      • 递归调用 get_combin([2,3,6,7], 3, 1, item, [2, 2])
    • 递归深度 4

      • 目标是 3,当前索引是 1,当前元素是 3。
      • 因为 3 - 3 = 0,等于 0,所以这是一个有效的组合。
      • path 变为 [2, 2, 3]
      • [2, 2, 3] 添加到 item 中。
    • 递归深度 3 回溯:

      • path 中移除最后一个元素,path 变为 [2, 2]
      • 移动到下一个元素,索引变为 1。
      • 之后的[2, 2, 6][2, 2, 7]都不行
      • 此时,索引3已经遍历完毕,没有更多的元素可以添加到 path 中。
    • 递归深度 2 回溯:

      • path 中移除最后一个元素,path 变为 [2]
      • 移动到下一个元素,索引变为 1。
    • 递归深度 2

      • 目标是 5,当前索引是 1,当前元素是 3。
      • 因为 5 - 3 = 2,大于 0,所以继续递归。
      • path 变为 [2, 3]
      • 递归调用 get_combin([2,3,6,7], 2, 1, item, [2, 3])
    • 递归深度 3

      • 目标是 2,当前索引是 1,当前元素是 3。
      • 因为 2 - 3 < 0,所以不继续递归。
      • 回溯到递归深度 2。
    • 递归深度 2 回溯:

      • path 中移除最后一个元素,path 变为 [2]
      • 移动到下一个元素,索引变为 2。
    • 递归深度 2

      • 目标是 5,当前索引是 2,当前元素是 6。
      • 因为 5 - 6 < 0,所以不继续递归。
      • 回溯到递归深度 1。
    • 递归深度 1 回溯:

      • path 中移除最后一个元素,path 变为 []
      • 移动到下一个元素,索引变为 1。
    • 递归深度 1

      • 目标是 7,当前索引是 1,当前元素是 3。
      • 因为 7 - 3 = 4,大于 0,所以继续递归。
      • path 变为 [3]
      • 递归调用 get_combin([2,3,6,7], 4, 1, item, [3])
    • 递归深度 2

      • 目标是 4,当前索引是 1,当前元素是 3。
      • 因为 4 - 3 = 1,大于 0,所以继续递归。
      • path 变为 [3, 3]
      • 递归调用 get_combin([2,3,6,7], 1, 1, item, [3, 3])
    • 递归深度 3

      • 目标是 1,当前索引是 1,当前元素是 3。
      • 因为 1 - 3 < 0,所以不继续递归。
      • 回溯到递归深度 2。
    • 递归深度 2 回溯:

      • path 中移除最后一个元素,path 变为 [3]
      • 移动到下一个元素,索引变为 2。
    • 递归深度 2

      • 目标是 4,当前索引是 2,当前元素是 6。
      • 因为 4 - 6 < 0,所以不继续递归。
      • 回溯到递归深度 1。
    • 递归深度 1 回溯:

      • path 中移除最后一个元素,path 变为 []
      • 移动到下一个元素,索引变为 2。
    • 递归深度 1

      • 目标是 7,当前索引是 2,当前元素是 6。
      • 因为 7 - 6 = 1,大于 0,所以继续递归。
      • path 变为 [6]
      • 递归调用 get_combin([2,3,6,7], 1, 2, item, [6])
    • 递归深度 2

      • 目标是 1,当前索引是 2,当前元素是 6。
      • 因为 1 - 6 < 0,所以不继续递归。
      • 回溯到递归深度 1。
    • 递归深度 1 回溯:

      • path 中移除最后一个元素,path 变为 []
      • 移动到下一个元素,索引变为 3。
    • 递归深度 1

      • 目标是 7,当前索引是 3,当前元素是 7。
      • 因为 7 - 7 = 0,等于 0,所以这是一个有效的组合。
      • path 变为 [7]
      • [7] 添加到 item 中。
  4. 返回结果:所有可能的组合已经被找到并添加到 item 中,函数返回 item

最终,item 数组包含 [[2,2,3],[7]],这是所有可能的组合,它们的和等于目标值 7。

注意点

在JavaScript中,数组是通过引用传递的,这意味着当你将一个数组作为参数传递给函数时,函数内部对该数组的任何修改都会影响原始数组。在代码中,path数组是用来记录当前递归路径上选择的元素。

if (target == 0) {path = path.slice()item.push(path);return
}

这里的关键是理解path数组在递归调用中是如何被修改的:

  1. 递归调用中的修改:每次递归调用get_combin函数时,都会向path中添加新的元素(path.push(candidates[i]))。如果没有复制path,那么当你在递归调用后返回上一层递归时,path将保留着当前递归层的所有元素。

  2. 回溯:在每次递归调用后,你通过path.pop()移除最后一个元素,以便尝试其他可能的元素。但是,如果你没有在将path添加到item之前复制它,那么当你从path中移除元素时,你实际上也在修改你刚刚添加到item的路径。

  3. 复制的必要性:为了解决这个问题,你需要在将path添加到item之前复制它。这样,你就可以在不影响原始path的情况下,将当前路径的状态保存下来。path.slice()创建了path的一个浅拷贝,这意味着item中的每个路径都是独立的,对它们的修改不会影响到其他路径。

举个例子:

假设candidates = [1, 2, 3]target = 4

  • 递归深度 1path = [],选择1,path = [1]target = 3
  • 递归深度 2path = [1],选择2,path = [1, 2]target = 2
  • 递归深度 3path = [1, 2],选择1,path = [1, 2, 1]target = 1

如果没有复制path,当你在深度3的递归调用后返回到深度2时,path将是[1, 2, 1],而不是[1, 2]。这将导致你错误地尝试使用[1, 2, 1]来找到剩余的target值,而不是重新从[1, 2]开始。

通过使用path.slice(),你可以确保每次将path添加到item时,都是添加的一个独立副本,这样在后续的递归和回溯中对path的修改就不会影响已经保存的路径。

相关文章:

LeetCode组合总和

题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同一个 数字可以 无限制重复被…...

MATLAB - 机械臂手眼标定(眼在手内) - 估计安装在机器人上的移动相机的姿态

系列文章目录 前言 本示例展示了如何为装有手眼构型摄像头的机械臂或机械手执行和验证手眼校准。 一、概述 执行手眼校准有助于操作配备末端执行器&#xff08;简称 “手”&#xff09;的机械臂&#xff0c;该末端执行器依赖于摄像头提供的视觉数据。一旦完成了眼在手外的校准&…...

【Unity】TextMeshPro 3.0.9无法显示emoji表情问题

需要下载TextMeshPro 3.2.x-pre.xxx版本&#xff0c;重新生成Sprite Asset文件解决 注意&#xff1a;若Package Manager没有搜到pre版本&#xff0c;那么可以去github下载到本地&#xff0c;再解压后&#xff0c;将文件夹移动到工程Packages文件夹下&#xff0c;然后打开Packa…...

金九银十软件测试面试题(800道)

今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#xff0c;人手一份 肝完进大厂 妥妥的&#…...

中国剩余定理 C++

题目 解题思路 原链接&#xff1a;https://www.acwing.com/solution/content/3539/ 大致步骤&#xff1a; 将第2,3,4…n个方程不断与第一个方程合并&#xff0c;得到方程a1k1a2k2m2-m1;用扩展欧几里得算法解出a1k1a2k2gcd(a1, a2)的结果&#xff0c;再将结果扩大(m2-m1)/d倍即…...

动态规划lc

先找到规律&#xff0c;然后找边界情况&#xff1b;部分特殊情况分类讨论 *递归 70.爬楼梯 简单 提示 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a…...

介绍xshell的使用技巧

使用技巧目录 1. 开启左键选中即复制&#xff0c;右键点击即粘贴2. 开启撰写功能3. 开启日志记录功能 1. 开启左键选中即复制&#xff0c;右键点击即粘贴 参考&#xff1a;https://blog.csdn.net/chirrupy_hamal/article/details/108619262 2. 开启撰写功能 使用场景&#x…...

揭秘语音识别巨头1:国内外顶尖技术服务商全解析01(万字长文)

一、学习导航 解密语音识别巨头&#xff1a;国内顶尖技术服务商全解析00&#xff1a;学习地图 解密语音识别巨头&#xff1a;国内顶尖技术服务商全解析01&#xff1a;微软语音&#xff0c;商业No.1 解密语音识别巨头&#xff1a;国内顶尖技术服务商全解析02&#xff1a;百度…...

JAVA使用SM2算法生成密钥对加密解密加签验签

简介 SM2是非对称加密算法&#xff0c;一提非对称加密算法&#xff0c;第一想到的是RSA&#xff0c;没错&#xff0c;这个就是替代RSA的。它是基于椭圆曲线密码的公钥密码算法标准&#xff0c;其秘钥长度256bit&#xff0c;包含数字签名、密钥交换和公钥加密&#xff0c;用于替…...

uniapp(vue)打包web项目页面刷新后报404解决方案

一、问题概述 uniapp是一款优秀的跨平台开发框架&#xff0c;它可以帮助开发者快速构建出适用于多端的应用程序。然而&#xff0c;在项目打包后&#xff0c;有可能发现页面在刷新时会出现404错误。这无疑给用户体验带来了极大的困扰&#xff0c;下面我们就来分析一下这个问题。…...

ansible学习之ansible-vault

相关文档参考:http://www.ansible.com.cn/docs/playbooks_vault.html#what-can-be-encrypted-with-vault ansible-vault 功能介绍 Ansible-Vault是一个用于加密和管理Ansible playbook中敏感数据的工具。通过创建、编辑、加密、解密、查看和重置密码&#xff0c;可以安全地存储…...

封装el-upload组件,用于上传图片和视频的组件

使用环境 vue3element plus 需要根据后端返回结构修改的函数&#xff1a;onPreview onRemove onSuccess 组件使用 基本使用 源代码&#xff1a; <script setup> import AutoUploadFile from /components/auto-upload-file/index.vue function change(urls){console.log…...

6.将扩散模型与其他生成模型的关联(2)

1.归一化流与扩散模型 自一化流(Normalizing Flow)是生成模型&#xff0c;通过将易于处理的分布进行变换以队对高维数据进行建模。归一化流可以将简单的概率分布转化为极其复杂的分布&#xff0c;并用于强化学习、变分推理等领域。 现有的归一化流是基于变量替换公式构…...

【C++】基于红黑树封装set和map

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、更高维度的泛型二、模版参数三、比较逻辑的重写四、迭代器4.1 const迭代器4.2 重载4.3 - -重载 五、完整代…...

24最新新手入门指南:Stable Diffusion!

前言 Stable Diffusion&#xff0c;一款新兴的开源AI绘画软件&#xff0c;正逐渐成为数字艺术家和爱好者的新宠。它的强大功能让用户能够轻松创造出令人印象深刻的数字艺术作品。 无论你是专业艺术家还是艺术新手&#xff0c;Stable Diffusion都为你提供了一个探索创造力的新…...

Java-基础

1. 导入模块不能纯粹的复制粘贴&#xff0c;要从new里导入&#xff0c;因为前者建立不了关联 2. 数组 String[] name{"张三","李四","王五"};int[] numsnew int[]{1,2,3};//二维String[][] names{{"张三","李四"},{"…...

二、后台管理系统布局菜单可拖动

前两天产品提出了一个需求,说后台管理系统的左边菜单的名称字数过多,遮挡了。希望能让客户能够看到全部的名称,给左侧菜单增加一个可拖动的功能,经过我的研究,这个功能最终也做出来了,先看效果,双击查看。 下面咱们进入实现步骤 第一步,找到文件。一般的项目中都存在l…...

socket和http区别

socket和http区别&#xff1a;1、主体不同&#xff1b;2、所处层次不同&#xff1b;3、连接状态不同&#xff1b;4、传输数据量不同&#xff1b;5、数据安全性不同&#xff1b;6、连接方式不同。其中&#xff0c;主体不同指的是socke是一个调用接口&#xff08;API&#xff09;…...

算法:974.和可以被K整除的子数组

题目 链接:leetcode链接 思路分析&#xff08;前缀和 同余定理&#xff09; 首先&#xff0c;我们要了解一下什么是同余定理 同余定理&#xff1a; 如果&#xff08;a - b&#xff09;/ p k …… 0 则 a % p b % p 证明我写在草稿纸上&#xff0c;如下图&#xff1a; 初…...

QD1-P8 HTML 格式化标签(font、pre、b、strong、i、u、del、s、sub、sup)

本节学习&#xff1a;HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 ‍ 一、font 标签 用途&#xff1a;定义文本的字体大小、颜色和 face&#xff08;字体类型&#xff09;。 示例 <!DOCTYPE html> <html><head><meta cha…...

红米Turbo 3工程固件预览 修复底层 体验原生态系统 默认开启diag端口

红米Turbo 3机型代码:peridot 国外版本:POCO F6 用于以下型号的小米机型:24069RA21C, 24069PC21G, 24069PC21I。搭载1.5K OLED屏、骁龙8s处理器、5000mAh电池+90W快充、5000万像素主摄。 通过博文了解 1💝💝💝-----此机型工程固件的资源刷写注意事项 2💝💝�…...

sql的调优指南及高级sql技巧

SQL调优是优化数据库性能的重要手段&#xff0c;涉及编写高效的SQL查询、合理设计索引、优化数据库结构等。以下是一些SQL调优指南和高级技巧&#xff1a; SQL调优指南 选择合适的查询方式&#xff1a; **避免使用SELECT ***&#xff1a;仅选择所需的列&#xff0c;减少数据传…...

生成式专题的第一节课---GAN图像生成

一、GAN的起源与发展 1.GAN的起源 GAN &#xff08;生成式对抗网络&#xff09;诞生于 2014 年&#xff0c;由 Ian Goodfellow 提出&#xff0c;是用于生成数据的深度学习模型&#xff0c;创新点是对抗性训练&#xff0c;即生成器与判别器的竞争关系&#xff0c;为图像生成、…...

中科星图GVE(案例)——AI实现建筑用地变化前后对比情况

目录 简介 函数 gve.Services.AI.ConstructionLandChangeExtraction(image1,image2) 代码 结果 知识星球 机器学习 简介 AI可以通过分析卫星图像、航拍影像或其他地理信息数据&#xff0c;实现建筑用地变化前后对比。以下是一种可能的实现方法&#xff1a; 数据获取&am…...

Spring Boot中获取application.yml中属性的几种方式

在Spring Boot应用程序中&#xff0c;可以通过多种方式从application.yml文件中获取配置属性。以下是几种常见的方法&#xff1a; 1. 使用Value注解 你可以使用Value注解将application.yml中的属性注入到Spring管理的bean中。 application.yml app:name: MySpringBootAppve…...

YOLO11改进 | 注意力机制 | 结合静态和动态上下文信息的注意力机制

秋招面试专栏推荐 &#xff1a;深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转 &#x1f4a1;&#x1f4a1;&#x1f4a1;本专栏所有程序均经过测试&#xff0c;可成功执行&#x1f4a1;&#x1f4a1;&#x1f4a1; 上下文Transformer&#xff08;CoT&…...

Python中函数的使用方法

1 问题 在python的学习中&#xff0c;一个相同的程序可能会有多种不同的代码输入方式&#xff0c;那么函数这种方式是否方便快捷呢&#xff1f;今天我们来简单介绍函数的部分使用方法。 2 方法 定义函数&#xff1a;代码清单1Def function name (arguments):return result在上面…...

遨游智能终端赋能“危急特”场景,力推北斗技术规模化应用!

随着《北斗规模应用三年行动计划&#xff08;2023-2025&#xff09;》的发布&#xff0c;北京、湖北、重庆等多地出台北斗支持政策&#xff0c;北斗系统正稳步迈向“安全可控&#xff0c;泛在融合&#xff0c;开放兼容&#xff0c;服务全球”的发展目标。遨游通讯紧跟国家战略步…...

构建流媒体管道:利用 Docker 部署 Nginx-RTMP 从 FFmpeg RTMP 推流到 HLS 播放的完整流程

最近要实现一个类似导播台的功能&#xff0c;于是我先用 FFmpeg 实现一个参考对照的 Demo&#xff0c;我将其整理为一篇文章&#xff0c;方便后续大家或者和自己参考&#xff01; 1、软件工具介绍 本次部署相关软件 / 工具如下&#xff1a; FFmpeg&#xff1a;全称是 Fast Fo…...

【汇编语言】寄存器(CPU工作原理)(六)—— 修改CS,IP的指令以及代码段

文章目录 前言1. 修改CS、IP的指令2. 问题分析:CPU运行的流程3. 代码段小结结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计…...