回溯算法-组合问题-力扣第77题

什么是组合问题——

举例:

有一个集合:{1,2,3,4}

现在要找出这个集合里面所有组合大小(组合长度,组合里面数据的个数)为2的组合

那么就有:总共6种组合

{1,2},{1,3},{1,4}

            {2,3},{2,4}

                        {3,4}

注意:

组合问题是无序的,即{1,2}={2,1},只算一种

排序问题是有序的,{1,2}和{2,1}是算两种

题目:

给定两个整数 n 和 k,返回 1 到 n 中所有可能的 k 个数的组合。

这里n指原组合大小长度(即所给组合中数据的个数),也就是sizeof(数组)

如{1,2,3,4},则n=4

k指需要输出的新组合的大小

如要找出这个集合里面所有组合大小(组合长度,组合里面数据的个数)为2的组合

则k=2

那么就有:总共6种组合

{1,2},{1,3},{1,4}

            {2,3},{2,4}

                        {3,4}

示例: 输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]

先是直接也暴力的查找方法,for循环,挨个找,然后经过for循环里面的操作进行组合,完成后打印输出 i j 就是要求的组合

如果n为100,k为50呢,那就50层for循环,是不是开始窒息。

此时就会发现虽然想暴力搜索,但是用for循环嵌套连暴力都写不出来!

咋整——为什么要用回溯法

回溯搜索法来了,虽然回溯法也是纯暴力方式,但至少能写出来,不像for循环嵌套k层让人绝望。

那么回溯法怎么暴力搜呢?

上面我们说了要解决 n为100,k为50的情况,暴力写法需要嵌套50层for循环,

那么回溯法就用递归来解决嵌套层数的问题——

回溯算法通过递归,来控制有多少层for循环

递归里的每一次其实是一个for循环,然后下一个递归就是下一层for循环

递归来做层叠嵌套(可以理解是开k层for循环),每一次的递归中嵌套一个for循环,那么递归就可以用于解决多层嵌套循环的问题了

此时递归的层数大家应该知道了,例如:n为100,k为50的情况下,就是递归50层。

如何使用回溯算法解决问题——一般能用回溯算法解决的问题,我们都可以把抽象为一个树形结构(N叉树)

用树形结构来理解回溯,模拟回溯搜索的过程,就容易多了

可以看出这棵树,一开始集合是 1,2,3,4, 从左向右取数,取过的数,不再重复取。

第一次取1,集合变为2,3,4 ,因为k为2,我们只需要再取一个数就可以了,分别取2,3,4,得到集合[1,2] [1,3] [1,4],以此类推。

每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围

直到取4时,可选择范围为空了,那么明显该分支是一个得不到结果的分支了,就应该是被叉掉的分支了,后面回说剪枝问题操作

图中可以发现n相当于树的宽度,k相当于树的深度

那么如何在这个树上遍历,然后收集到我们要的结果集呢

图中每次搜索到了叶子节点,我们就找到了一个结果

相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。

注意:求组合问题,第一组合无序,前面选择过了,后面就不选择了

第二组合里的元素不可以重复使用,即不会出现{1,1},{2,2},{3,3}等的情况

所以在取1的集合中,可选择的范围就是数据1之后,后面的所有数据;取2就是2后面的

那么如何达到这个效果,在后面每次递归通过传入一个参数,起名叫【startIndex】,来控制每次搜索的起始位置

回溯三部曲

递归里面嵌套for循环,for循环里面再嵌套递归,会很懵

所以有一个回溯算法的模版,其分为3个部分

回溯靠递归来实现,所以回溯函数就是递归函数

下来我们要确定以下3部分

  • 递归函数的返回值以及参数
  • 确定递归的终止条件(终止条件处理不好的话,递归就是一个死循环)
  • 确定单层搜索的逻辑(即单层递归的逻辑)

一.递归函数的返回值以及参数

递归函数的返回值一般情况下都是void,但在求回溯算法的时候,递归函数才有返回值

函数名字通常情况大家都叫backtracking

void  backtracking(        )

下来()内就要确定参数了,看参数有哪些

首先一个组合,可以理解看成是一个一维数组,给它取名叫做path,

即树形结构中路径的意思

可以看到,最终我们求到的组合结果,就是一个个叶子结点,即一条条路径顺下来的结果

其就是一个收集路径的过程

那么这个组合怎么取,怎么存放

我们还需要定义一个二维数组(名字叫做result)——把整个,所有收集来的组合,存放在一起,

然后以一个结果集的方式,返回回去——(这也是力扣题中要求的返回结果的方式)

这里的path和result,都定义为全局变量;

也可以是放到()中作参数,给它定义成一个引用的方式,但这样会让参数过多,影响代码的可读性,

所以还是放到全局变量里

继续看参数里还需要什么——总共3个

首先需要n和k,然后就是我们刚说的startIndex(每次搜索前都传进来一个当前的起始位置)

二.确定递归的终止条件

通过树形结构可以直观看到,每个叶子结点(没有孩子的结点)就是我们想要的结果,即终止

那么如何到叶子结点呢,前面我们用pass收集路径结果组合,而我们想要的组合大小k为2

那么当pass的大小等于k时,说明找到了一个大小为k的组合了,则不用路径再往下走了,此时的叶子结点就是结果

终止条件:

if(pass.size==k)

{   

//收割叶子结点的结果 ,此时pass里面存放的已经是某条路径下的叶子组合结果了

     result数组收集

return

}

三.确定单层搜索的逻辑

在树形结构里,每一个结点都是一个for循环,它的起始位置(是1还是2,3,4)由startIndex决定开始

在单层搜索的每一个i里面,当前的i都有pass数组来收集这个路径上的元素叶子结点

然后从当前i开始递归,其纵向的下一层的startIndex从i+1开始

最后回溯,pass撤销处理的结点,把之前收集的结果弹出去

举例说明:

刚收集了12,那么接下来就要把2弹出去,剩下1,再跟新加进来的3,组合成13,同理:14

所以回溯(pop)就是再回到1没跟2组合之前还是1的状态

如果没有回溯的过程,一直是递归之下,那么它就会一直往里面加数据,就是1234而不是12,13,14了

12,13,14完成之后就把4弹出,再把1弹出,把2加进来,把3加进来,组合23,谈3进4,24,依次类推

也就是上图中取2,取3,取4类似于画英文字母M的过程,下来再上去再下来,就是树形结构选取每个分支的过程

​​​​​​​

回溯算法模版

剪枝优化

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小猪APP分发:掌握封装应用的艺术,优化用户体验

在移动应用市场日益激烈的竞争环境中,小猪APP分发www.ppzhu.net脱颖而出,成为众多开发者信赖的伙伴。它不仅简化了应用的发布流程,更通过其核心的封装技术,有效解决了应用分发中的一系列痛点,为用户带来无缝、高效的下…

Python从0到POC编写--函数

数学函数: 1. len len() 函数返回对象(字符、列表、元组等)长度或项目个数, 例如: str "python" len(str)2. range range() 函数返回的是一个可迭代对象(类型是对象),…

Nios实验入门——用Verilog编程方式完成LED流水灯显示并使用串口输出“Hello Nios-II”字符到笔记本电脑

文章目录 前言一、Verilog编程方式完成LED流水灯显示1.1 新建工程并添加FPGA芯片1.2 新建.v文件并添加至顶层实体1.3 引脚分配1.4 编译(包含分析与综合)1.5 选择烧录器1.6 添加烧录文件1.7 下载1.8 实验现象 二、Verilog编程方式实现串口2.1 uart_tx.v文件2.2 test.v文件2.3 to…

【MySQL】sql表设计的注意事项

程序员的实用神器 文章目录 程序员的实用神器强烈推荐引言注意事项强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能 推荐一个个人工作&#x…

Android面试题之Kotlin的apply、let、also、run函数

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点 apply apply函数可以看作是一个配置函数,可以传入一个接收者,然后调用一系列函数来配置以便使用,如果提供l…

Redis-数据过期策略

文章目录 Redis数据持久化策略的作用是什么?Redis的数据过期策略有哪些?惰性删除定期删除 更多相关内容可查看 Redis数据持久化策略的作用是什么? Redis数据过期策略是指在Redis中设置数据的过期时间,并在数据过期时自动从数据库…

《安富莱嵌入式周报》第336期:开源计算器,交流欧姆表,高性能开源BLDC控制器,Matlab2024a,操作系统漏洞排名,微软开源MS-DOS V4.0

周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 本周更新一期视频教程: BSP视频教程第30期:UDS ISO14229统一诊断服务CAN总线专题,常…

【SRC实战】无限获取优惠码

挖个洞先 https://mp.weixin.qq.com/s/HgMK4S8275VvFVbnSp6Qsw “ 以下漏洞均为实验靶场,如有雷同,纯属巧合 ” 01 — 漏洞证明 “ 获取优惠码有次数限制的情况下,如何绕过?” 1、新用户专属福利,免费领100元优惠…

聚观早报 | 小米与京东达成合作;比亚迪销量全球第一

聚观早报每日整理最值得关注的行业重点事件,帮助大家及时了解最新行业动态,每日读报,就读聚观365资讯简报。 整理丨Cutie 5月14日消息 小米与京东达成合作 比亚迪销量全球第一 vivo X100s关键参数曝光 小鹏汽车进军澳大利亚市场 腾讯Q…

(接上一篇linux rocky 搭建DNS高阶版)实现不同网段访问解析不同的服务器并加域

上一篇链接:linux rocky 搭建DNS服务和禁止AD域控DNS,做到独立DNS并加域-CSDN博客文章浏览阅读417次,点赞13次,收藏7次。使用linux rocky 搭建DNS服务,用于独立AD域控DNS存在,并且实现加域。https://blog.c…

vue3修改eldialog弹窗css不生效

问题:子组件中的eldialog没有父标签 直接使用如下是不生效的 .el-dialog{ top: 10%; } 解决: 加一个父标签 使用deep深度查询 .dialogClass :deep(.el-dialog) { top: 10%; } 就可以修改了

「JavaEE」多线程案例分析2:实现定时器

🎇个人主页:Ice_Sugar_7 🎇所属专栏:JavaEE 🎇欢迎点赞收藏加关注哦! 实现定时器 🍉简介🍉模拟实现定时器 🍉简介 定时器类似一个闹钟,时间到了之后就会执行…