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

学习记录:js算法(八十二):组合总和

文章目录

    • 组合总和
      • 思路一
      • 思路二
      • 思路三

组合总和

给你一个 无重复元素 的整数数组 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
输出: []

思路一

function combinationSum(candidates, target) {const res = [];const backtrack = (path, sum, startIndex) => {if (sum === target) {res.push([...path]);return;}if (sum > target) {return;}for (let i = startIndex; i < candidates.length; i++) {path.push(candidates[i]);sum += candidates[i];backtrack(path, sum, i); // 注意这里是i,允许重复选取同一个元素path.pop();sum -= candidates[i];}};backtrack([], 0, 0);return res;
}

讲解
这道题目可以通过使用回溯算法(Backtracking)来解决。回溯算法是一种通过试探搜索(试错法)来寻找所有可能的解决方案的算法。

  1. 初始化:创建一个空的路径数组path来存储当前的组合,以及一个空的结果数组res来存储所有有效的组合。
  2. 递归函数:定义一个递归函数backtrack,它接受以下参数:
    ○ 当前路径path,
    ○ 当前路径的和sum,
    ○ 下一个开始搜索的索引startIndex。
  3. 基本结束条件:如果当前路径的和sum等于target,将当前路径path拷贝一份加入结果数组res,然后返回。
  4. 剪枝条件:如果当前路径的和sum大于target,直接返回,不再继续探索。
  5. 回溯过程:对于startIndex到数组末尾的每一个数字,执行以下操作:
    ○ 将当前数字添加到路径path中,并更新路径的和sum。
    ○ 递归调用backtrack函数,传递更新后的path、sum和下一个开始搜索的索引。
    ○ 回溯,即将当前数字从路径path中移除,并恢复路径的和sum。
  6. 开始回溯:调用backtrack函数,传入初始的path、sum和startIndex。
  7. 返回结果:最后返回结果数组res。

思路二

var combinationSum = function (candidates, target) {const dp = Array.from({ length: target + 1 }, () => []);dp[0] = [[]]; // 目标为0时,只有一种组合:空数组for (const num of candidates) {for (let i = num; i <= target; i++) {for (const combination of dp[i - num]) {dp[i].push([...combination, num]); // 添加当前数字}}}return dp[target];
};

讲解

  1. 定义函数:combinationSum 是一个接受两个参数的函数,candidates 是可选的数字数组,target 是我们想要达到的目标和。
  2. 创建动态规划数组:dp 是一个二维数组,其中 dp[i] 存储所有和为 i 的组合。我们初始化一个长度为 target + 1 的数组,每个元素都是一个空数组。
  3. 初始化基例:dp[0] 被初始化为包含一个空数组的数组,表示和为 0 的组合是唯一的,即不选任何数字。
  4. 循环:
    • 外层循环:遍历每个候选数字 num。
    • 中层循环:从 num 开始,遍历到 target。这是因为我们只需考虑从 num 开始的目标值,因为小于 num 的目标值无法包含 num。
    • 内层循环:遍历 dp[i - num] 中的每个组合。dp[i - num] 表示在目标值为 i - num 时的所有组合。
      • 组合构建:对于每个组合,我们创建一个新的组合,将当前数字 num 添加到其中,并将其推入 dp[i] 中。
  5. 返回结果:最后,函数返回 dp[target],即所有和为 target 的组合。

思路三

var combinationSum = function (candidates, target) {const result = [];const stack = [{ combination: [], remaining: target, start: 0 }];while (stack.length) {const { combination, remaining, start } = stack.pop();if (remaining === 0) {result.push(combination);continue;}for (let i = start; i < candidates.length; i++) {if (remaining < candidates[i]) continue;stack.push({ combination: [...combination, candidates[i]], remaining: remaining - candidates[i], start: i });}}return result;
};

讲解

  1. 函数定义:combinationSum 是一个接受两个参数的函数,candidates 是可选的数字数组,target 是我们想要达到的目标和。
  2. 初始化结果和栈:
    • result 是一个数组,用于存储所有满足条件的组合。
    • stack 是一个栈,用于模拟递归过程。初始时,栈中包含一个对象,表示当前的组合为空(combination: [])、剩余目标值为 target(remaining: target),并且从候选数组的起始位置开始(start: 0)。
  3. 主循环:当栈不为空时,进入循环,弹出栈顶元素,获取当前的组合、剩余目标值和起始索引。
  4. 检查剩余目标值:如果剩余目标值为0,说明当前组合的数字之和等于目标值,将该组合添加到结果中,并继续进行下一轮循环。
  5. 遍历候选数字:
    • 遍历候选数字,从当前的起始索引 start 开始。
    • 如果当前候选数字大于剩余目标值,则跳过该数字。
    • 否则,将当前候选数字添加到组合中,并更新剩余目标值(remaining - candidates[i]),同时将起始索引保持为 i,以允许重复使用当前候选数字。将这个新状态压入栈中。
  6. 返回结果:当栈为空时,所有可能的组合都已被处理,函数返回结果数组 result

http://www.mrgr.cn/news/63214.html

相关文章:

  • MacOS/Macbook用户自定义字体安装教程
  • .Net C# 基于EFCore的DBFirst和CodeFirst
  • 闲一品交易平台:SpringBoot技术的新境界
  • InstructIR: High-Quality Image Restoration Following Human Instructions 论文阅读笔记
  • Vscode配置CC++编程环境的使用体验优化和补充说明
  • 类和对象(中)—— 类的六个默认成员函数
  • 华为OD机试 - 快递员的烦恼 - 动态规划(Python/JS/C/C++ 2024 D卷 200分)
  • Halcon 2D测量Metrology找线/圆/矩形/椭圆
  • Git进阶(十七):特性分支
  • 用二维码展示信息,有哪些常见应用场景
  • Idea常用插件
  • 2025 年IT技术人员关键技能(非常详细),零基础入门到精通,看这一篇就够了
  • Go使用SIMD指令——以string转为整数为例
  • netty之bootstrap源码分析
  • Android 中选择本地文件并获取文件路径
  • BC1 2充电协议简介
  • JS进阶级案例-----时钟
  • Python零基础 [2.3] if else 语句的详解与示例
  • 《PHP爬虫:当“购物狂”遇上“代码诗人”》
  • 算子级血缘助企业数据管理“自动化、精细化、智能化”
  • Redis 中的定期删除和惰性删除究竟是怎样实现的?
  • flutter报错‘/Users/xxx/.gradle/caches/journal-1/file-access.bin‘.
  • 用图像增强来充实训练数据集,算不算是一种‘摸鱼’的方法?
  • 大型语言模型如何影响就业?大模型入门到精通,收藏这篇就够了
  • 初学者如何对大模型进行微调?
  • Openlayers高级交互(14/20):汽车移动轨迹动画(开始、暂停、结束)