Javascript算法——回溯算法(组合问题)

相关资料来自《代码随想录》,版权归原作者所有,只是学习记录
在这里插入图片描述

回溯

回溯模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

回溯搜索过程可以概括为一棵"树"

在这里插入图片描述
三数之和 四数之和 也有点类似求满足指定条件的数组

组合问题

组合与排列的区别

排列:排列是指从一组元素中选择若干个元素并考虑顺序,也就是说 [1, 2] 和 [2, 1] 是不同的排列。

组合:组合是指从一组元素中选择若干个元素,但不考虑顺序,因此 [1, 2] 和 [2, 1] 视为相同的组合。

1. 在这里插入图片描述————————————————————————————————在这里插入图片描述

核心:
1.递归,设置回溯函数参数(index),避免重复组合[1,2]与[2,1]
2.剪枝:i<=_n-(_k-path.length)+1,保证还有足够多元素(此题)
3.利用slice方法浅拷贝数组,而不是直接加result.push(path.slice())在这里插入图片描述

/**
 * @param {number} n
 * @param {number} k
 * @return {number[][]}
 */
var combine = function(n, k) {
    let result=[],path=[];
    let backtracking=(_n,_k,startIndex)=>{
        //终止条件
        if(path.length===_k){
            result.push(path.slice());
            return;
        }
        //循环本层集合元素
        //剪枝操作:i<=_n-(_k-path.length)+1
        for(let i=startIndex;i<=_n-(_k-path.length)+1;i++){
            path.push(i);
            //递归
            backtracking(_n,_k,i+1);
            //回溯操作
            path.pop();
        }
    };
        backtracking(n,k,1);
        return result  
};

2
在这里插入图片描述
———————————————————————————————

核心:
1.不重复,与上类似回溯函数设置一个索引参数index
2.数字1-9,想着去生成数组?->遍历就行for(let i=startIndex;i<9;i++),无需额外定义

/**
 * @param {number} k
 * @param {number} n
 * @return {number[][]}
 */
var combinationSum3 = function(k, n) {
    //确定参数和返回值
    //参数:k为递归树的层数,即数组path.length===k时就收集结果了!
    //n为收集数组元素和
    let result=[],path=[];
    let sum=0;
    let backtracking=(_k,_n,startIndex)=>{
        //确定递归终止条件
        if(path.length===_k){
            //数组元素之和不为n,直接返回
            if(sum===_n){
                //path.slice()!!!浅拷贝
                //[...path]
                result.push(path.slice());
            }
            return;
        }
        for(let i=startIndex;i<9;i++){
            sum+=i;
            path.push(i);
            //递归
            backtracking(_k,_n,i+1);
            //回溯
            sum-=i;
            path.pop();
        }
    }
    backtracking(k,n,1);
    return result; 
};

3.
在这里插入图片描述

核心
1.字母映射:用JS中数组就行!let letterMap=["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"];
2.还是利用数组存遍历的元素,返回结果时对结果做处理就行result.push(path.join(""));
3.本问题是对多个集合做组合问题,且每个集合中元素唯一。直接再遍历时处理就行!const v of letterMap[digits[_diIndex]]

/**
 * @param {string} digits
 * @return {string[]}
 */
var letterCombinations = function(digits) {
     //直接用for循环,那3个数,4个数嘞?回溯!!!

    //字母映射:用数组就行呀!!!
    let letterMap=["","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"];
   
    //确定回溯参数
    //还是用数组,好回溯,知识结果最后进行一下处理就行
    let result=[],path=[];

    let length=digits.length;
    if(length===0)return result;
    //字符串->字符数组(split)
    if(length===1)return letterMap[digits].split("");

    let backtracking=(_digits,_length,_diIndex)=>{
        if(path.length===_length){
            result.push(path.join(""));
            return;
        }
        //多个集合中进行递归与回溯!
        //集合->letterMap[digits[_diIndex]]
        for(const v of letterMap[digits[_diIndex]]){
            //从一个集合中加入一个字符
            path.push(v);
            //递归,索引控制向下一个集合遍历
            backtracking(_digits,_length,_diIndex+1)
            //回溯
            path.pop();
        }
    }
    backtracking(digits,length,0);
    return result;
};

4.
在这里插入图片描述
————————————————————————————————

核心:
1.集合中元素可以重复使用
2.简枝操作,当sum+item>target时就break!if(sum+item>target)break;
3.可重复: backtracking(i,sum,target);
在这里插入图片描述

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum = function(candidates, target) {
    //允许相同的,不应该更好,不考虑剪枝的就是直接多层for
    //参数与返回值
    candidates.sort((a,b)=>a-b); // 排序
    let res=[],path=[];

    let backtracking=(index,sum,target)=>{
        //终止条件
        if(sum>target)return;
        if(sum===target){
            res.push(path.slice());
            return;
        }
        for(let i=index;i<candidates.length;i++){
            let item=candidates[i];
            //剪枝 sum>target,相加大于无需再递归
            if(sum+item>target)break;
            sum+=item;
            path.push(item);
            //递归,//需考虑 [2,2,3],[2,3,2],[3,2,2]情况
            backtracking(i,sum,target);
            //回溯
            path.pop();
            sum-=item;
        }

    }
    backtracking(0,0,target);
    return res;
    
};

5.
在这里插入图片描述
————————————————————————————————

核心:
1.数组中包括相同元素,去重操作
2.去重先序排序 candidates.sort((a,b)=>a-b);
3.去重操作,与三数之和去重操作类似 if(j>i&&candidates[j]===candidates[j-1]){continue; //若当前元素和前一个元素相同 }

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function(candidates, target) {
    //核心:数组中包括相同元素,去重操作!三数求和当时也需考虑去重操作

    //参数和返回值
    let res=[],path=[],len=candidates.length;
    //!!!排序,去重时需要
    candidates.sort((a,b)=>a-b);
    
    let backtracking=(sum,i)=>{
        if(sum===target){
            res.push(path.slice());
            return;
        }
        for(let j=i;j<len;j++){
            const n=candidates[j];
            //!!!去重
            if(j>i&&candidates[j]===candidates[j-1]){
                //若当前元素和前一个元素相同
                //去重
                continue;
            }
            //剪枝
            if(sum+n>target)break;
            path.push(n);
            sum+=n;
            //递归
            backtracking(sum,j+1);
            //回溯
            path.pop();
            sum-=n;
        }
    }
    backtracking(0,0);
    return res;   
};

Further:组合问题概述

组合问题是经典的算法问题,通常涉及从一组元素中选择若干个元素,按照不同的规则生成所有符合条件的组合。根据问题的要求,组合问题可以按照不同的条件和约束进行分类。下面是一些常见的组合问题类型及其简要概述:

1. 组合的基本类型

这些问题通常要求从给定的一组元素中选择若干个元素,且不考虑顺序。

1.1. 组合总和问题(Combination Sum)
  • 描述:给定一个候选数字的集合和一个目标值,找到所有可以组合出目标值的数字组合。每个数字可以被无限次使用。
  • 典型问题
    • 组合总和(combinationSum):找到和为目标值的组合。
    • 组合总和 II(combinationSum2):与 combinationSum 类似,但数组中的元素是唯一的,不能重复选择。
1.2. 组合问题(Combinations)
  • 描述:从 n 个不同的元素中选择 k 个元素,返回所有可能的组合。选择时不考虑顺序。

  • 典型问题

    • n 个元素中选择 k 个元素的组合。
    // 示例:从 [1, 2, 3] 中选择 2 个元素
    combinations([1, 2, 3], 2);  // 结果:[ [1, 2], [1, 3], [2, 3] ]
    
1.3. 子集问题(Subsets)
  • 描述:从一组元素中找出所有的子集(包括空集)。该问题要求列出集合的所有组合,通常包括选择与不选择每个元素的情况。

  • 典型问题

    • 子集问题(subsets):给定一个整数数组,返回所有可能的子集。
    • 子集 II(subsetsWithDup):与 subsets 类似,但允许输入数组有重复元素。
    // 示例:给定 [1, 2, 3],输出所有子集
    subsets([1, 2, 3]);  // 结果:[ [], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3] ]
    

2. 带有额外约束的组合问题

这些问题通常包含额外的约束条件,如目标值、元素数量限制等。

2.1. 组合总和问题 II(Combination Sum II)
  • 描述:类似于组合总和问题(Combination Sum),但是输入数组中有重复元素,且每个数字只能使用一次。
  • 典型问题
    • 组合总和 II(combinationSum2):从一个包含重复数字的数组中,找出所有和为目标值的组合,每个数字只能用一次。
2.2. 组合求和问题(Combination Sum IV)
  • 描述:给定一个整数数组 nums 和目标整数 target,返回能够组合成目标值 target 的组合数。
  • 典型问题
    • 给定数组 nums = [1, 2, 3] 和目标 target = 4,返回能组成 target 的组合数(例如:[1, 1, 1, 1], [1, 3], [2, 2])。
2.3. 组合问题与选择的顺序有关(Permutations)
  • 描述:与组合不同,排列问题不仅考虑元素的选择,还要考虑元素的顺序。
  • 典型问题
    • 排列问题(permutations):从给定的数字中生成所有可能的排列。
    • 带重复元素的排列问题(permuteUnique):如果给定的数组包含重复元素,要求去除重复的排列。

3. 与排列、分配相关的组合问题

这类问题不仅仅关注组合,还涉及到排列或者将组合元素分配到不同的槽或组中。

3.1. 整数划分问题(Integer Partition)
  • 描述:将一个正整数分解为一组和为目标的正整数,可以允许元素重复。
  • 典型问题
    • 给定一个整数 n,将其分解为若干个正整数的和,可以多次使用同一个数字。
3.2. 组合分组问题(Combination Partition)
  • 描述:将一组数字分成若干个组合,每个组合的和为目标值。
  • 典型问题
    • 给定一个数组和目标值,将数组分成若干个子数组,使得每个子数组的和为目标值。
    • 例如,[1, 2, 3, 4],目标和为 5,应该如何划分出和为 5 的组合。

4. 与图相关的组合问题

这类问题涉及图结构,其中节点可以代表元素,边代表元素之间的关系,解空间的搜索通常采用深度优先搜索(DFS)或广度优先搜索(BFS)方法。

4.1. 图的遍历问题(Graph Traversal)
  • 描述:例如,通过图的回溯算法遍历图的节点以找到所有可能的组合。
  • 典型问题
    • 通过图的遍历找出从一个节点到另一个节点的所有路径。
    • 例如,找出所有从节点 A 到节点 B 的路径。

5. 动态规划与回溯结合的组合问题

这类问题结合了动态规划和回溯算法,通常用于处理大规模数据或优化问题。

5.1. 背包问题(Knapsack)
  • 描述:给定一组物品,每个物品有重量和价值,背包有最大承重,求最大价值的组合。
  • 典型问题
    • 01背包:每个物品只能选择一次,求能获得的最大价值。
    • 完全背包:每个物品可以选择多次,求最大价值。
5.2. 分割问题(Partition Problem)
  • 描述:给定一个整数数组,判断是否可以将其分割成两个子集,使得两个子集的和相等。
  • 典型问题
    • 子集和问题:判断给定数组是否可以分成两个和相等的子集。

6. 其他变种组合问题

还有一些组合问题可能带有更为复杂的条件或要求,比如:

6.1. 组合最大化问题
  • 描述:给定一个数组,选择若干个元素,使得它们的和尽量接近某个目标,或者在某些约束条件下求最大值。
6.2. 最大子集和问题
  • 描述:从给定的数组中选出若干个子集,使得它们的和最大,通常会使用动态规划进行优化。

总结:

组合问题的分类方法有很多,通常可以根据是否允许重复选择、是否考虑顺序、是否存在约束条件等多个方面进行划分。常见的组合问题包括:基础的子集与组合问题、带有约束的组合问题(如组合总和、分割问题等)、与排列、分配相关的问题(如背包问题、整数划分问题等)以及需要结合动态规划的复杂组合问题等。在处理这些问题时,回溯算法常常被用于解决那些穷举解空间的问题,而动态规划则适用于那些有子结构且可以优化的问题。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java Excel转PDF POI+Itext5

由于现在存在需求&#xff0c;通过Java将数据文本生成特点格式Excel&#xff0c;再输出为PDF。 调研了一些方案&#xff0c;最终决定使用POI写入Excel,再使用Itext5生成PDF。 在网上找了一些Itext的转换工具类&#xff0c;进行了一些改动。 目前市面上 Excel 转 PDF 的组件…

linux nginx maccms管理后台无法进入页面不存在和验证码不显示的问题

windows中运行maccms非常顺利&#xff0c;轻松搭建了。并一切正常。而我在linux中搭建缺遇到了一个非常奇怪的问题。进入管理后台&#xff0c;明明"admin.php"(比如重命名成a.php)的页面是存在的&#xff0c;访问时缺提示页面不存在&#xff01;稍后就自动跳到首页了…

C# 服务调用RFC函数获取物料信息,并输出生成Excel文件

这个例子是C#服务调用RFC函数&#xff0c;获取物料的信息&#xff0c;并生成Excel文件 上接文章&#xff1a;C#服务 文章目录 创建函数创建结构编写源代码创建批处理文件运行结果-成功部署服务器C#代码配置文件注意&#xff01;&#xff01; 创建函数 创建结构 编写源代码 创建…

在 SQL 中,区分 聚合列 和 非聚合列(nonaggregated column)

文章目录 1. 什么是聚合列&#xff1f;2. 什么是非聚合列&#xff1f;3. 在 GROUP BY 查询中的非聚合列问题示例解决方案 4. 为什么 only_full_group_by 要求非聚合列出现在 GROUP BY 中&#xff1f;5. 如何判断一个列是聚合列还是非聚合列&#xff1f;6. 总结 在 SQL 中&#…

Postman测试big-event

报错500。看弹幕&#xff0c;知道可能是yml或sql有问题。 所以检查idea工作台&#xff0c; 直接找UserMapper检查&#xff0c;发现完全OK。 顺着这个error发现可能是sql有问题。因为提示是sql问题&#xff0c;而且是有now()的那个sql。 之后通过给的课件&#xff0c;复制课件…

SpringBoot 2.6 集成es 7.17

引言 在现代应用开发中&#xff0c;Elasticsearch作为一个强大的搜索引擎和分析引擎&#xff0c;已经成为许多项目不可或缺的一部分。Spring Boot作为Java生态中最受欢迎的微服务框架之一&#xff0c;其对Elasticsearch的支持自然也是开发者关注的焦点。本文将详细介绍如何在S…

沙箱模拟支付宝支付3--支付的实现

1 支付流程实现 演示案例 主要参考程序员青戈的视频【支付宝沙箱支付快速集成版】支付宝沙箱支付快速集成版_哔哩哔哩_bilibili 对应的源码在 alipay-demo: 使用支付宝沙箱实现支付功能 - Gitee.com 以下是完整的实现步骤 1.首先导入相关的依赖 <?xml version"1…

自行下载foremos命令

文章目录 问题描述其他小伙伴的成功解决方案&#xff0c;但对我不适用解决思路失败告终 最终解决成功解决思路解决步骤 问题描述 在kali系统终端中输入foremost&#xff0c;显示无此命令 其他小伙伴的成功解决方案&#xff0c;但对我不适用 解决思路 正常来说使用命令 apt-g…

商米电子秤服务插件

概述 SunmiScaleUTS封装商米电子秤服务模块&#xff0c;支持商米旗下S2, S2CC, S2L CC等设备&#xff0c;设备应用于超市、菜市场、水果店等,用于测量商品的重量,帮助实现快捷、准确、公正的交易等一系列商业场景。 功能说明 SDK插件下载 一. 电子秤参数 型号:S2, S2CC, …

快速将索尼手机联系人导出为 HTML 文件

我想将 Sony Xperia 手机上的联系人导出到计算机上进行备份&#xff0c;并在需要时进行编辑。这可以做到吗&#xff1f;如何做到&#xff1f;作为助手我需要下载什么工具吗&#xff1f; 当您的 Android 手机上存储了如此多的重要联系人&#xff0c;而您又不想丢失它们时&#…

linux安装redis及Python操作redis

目录 一、Redis安装 1、下载安装包 2、解压文件 3、迁移文件夹 4、编译 5、管理redis文件 6、修改配置文件 7、启动Redis 8、将redis服务交给systemd管理 二、Redis介绍 1、数据结构 ①字符串String ②列表List ③哈希Hash ④集合Set ⑤有序集合Sorted Set 2、…

聆听音乐 1.5.9 | 畅听全网音乐,支持无损音质下载

聆听音乐手机版是面向广大音乐爱好者的移动应用程序&#xff0c;用户可以随时随地通过手机享受丰富的音乐资源。它提供了多种魅力功能&#xff0c;让用户在手机上畅享更舒适的音乐体验&#xff0c;每位用户都能享受精彩纷呈的收听体验。此外&#xff0c;软件还支持无损音质音乐…

在React中引入tailwind css(图文详解)

Tailwind CSS 是一个功能强大的 CSS 框架&#xff0c;旨在使开发者能够以更高效、灵活的方式创建现代、响应式的网页。与传统的 CSS 框架&#xff08;如 Bootstrap 或 Foundation&#xff09;不同&#xff0c;Tailwind 采取了“实用类”&#xff08;Utility-First&#xff09;的…

双指针算法详解

目录 一、双指针 二、双指针题目 1.移动零 解法&#xff1a; 代码&#xff1a; 2.复写零 ​编辑 解法&#xff1a; 代码&#xff1a; 边界情况处理: 3.快乐数 ​编辑 解法:快慢指针 代码&#xff1a; 4.盛水最多的容器 解法&#xff1a;&#xff08;对撞指针&#xff09;…

每天40分玩转Django:Django Celery

Django Celery 一、知识要点概览表 模块知识点掌握程度要求Celery基础配置、任务定义、任务执行深入理解异步任务任务状态、结果存储、错误处理熟练应用周期任务定时任务、Crontab、任务调度熟练应用监控管理Flower、任务监控、性能优化理解应用 二、基础配置实现 1. 安装和…

Web安全扫盲

1、建立网络思维模型的必要 1 . 我们只有知道了通信原理&#xff0c; 才能够清楚的知道数据的交换过程。 2 . 我们只有知道了网络架构&#xff0c; 才能够清楚的、准确的寻找漏洞。 2、局域网的简单通信 局域网的简单通信&#xff08;数据链路层&#xff09; 一般局域网都通…

【MATLAB APP Designer】小波阈值去噪(第一期)

代码原理及流程 小波阈值去噪是一种信号处理方法&#xff0c;用于从信号中去除噪声。这种方法基于小波变换&#xff0c;它通过将信号分解到不同的尺度和频率上来实现。其基本原理可以分为以下几个步骤&#xff1a; &#xff08;1&#xff09;小波变换&#xff1a;首先对含噪信…

CDP集群安全指南-动态数据加密

[〇]关于本文 集群的动态数据加密主要指的是加密通过网络协议传输的数据&#xff0c;防止数据在传输的过程中被窃取。由于大数据涉及的主机及服务众多。你需要更具集群的实际环境来评估需要为哪些环节实施动态加密。 这里介绍一种通过Cloudera Manager 的Auto-TLS功能来为整个…

信息安全、网络安全和数据安全的区别和联系

1. 前言 有次有朋友问我 信息安全、网络安全和数据安全&#xff0c;这三个词平时写文档时怎么用&#xff1f; 我想很多人都说不清。这次我查阅了资料&#xff0c;尽量讲清楚这三者之间的区别和联系。 2. 信息安全 2.1 定义 信息安全是指为数据处理系统建立和采用的技术和管…

vim 的基础使用

目录 一&#xff1a;vim 介绍二&#xff1a;vim 特点三&#xff1a;vim 配置四&#xff1a;vim 使用1、vim 语法格式2、vim 普通模式&#xff08;1&#xff09;保存退出&#xff08;2&#xff09;光标跳转&#xff08;3&#xff09;文本删除&#xff08;4&#xff09;文本查找&…