算法套路十一 ——回溯法之组合型回溯

news2024/11/16 20:31:26

算法套路十一 ——回溯法之组合型回溯

  • 该节是在上一节回溯法之子集型回溯的基础上进行描写,组合型回溯会在子集型回溯的基础上判断所选子集是否符合组合要求, 故请首先阅读上一节算法套路十——回溯法之子集型回溯

算法示例:LeetCode77. 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。在这里插入图片描述

可以按照上一节子集型回溯的想法分为两种思路,不过组合型回溯的区别在于对子集是否加入ans中有限制,如本题就要求子集中的个数为2,因此需要在递归时判断子集的个数,由此可在递归时判断并进行剪枝。
在这里插入图片描述
剪枝:对于本题,为考虑方便,我们可以从n开始进行选择,设path长为m,那么还需要选d=k - m个数设当前需要从[1,i]这i个数中选数,如果i<d,最后必然无法选出k个数不需要继续递归,如上图右边的图,k=3,如果我们目前选择的是2,且为避免重复,我们之后选择的数都要比2小,那可知不可能满足k=3,所以该递归路径可以直接结束。

法一:选或不选

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []
        def dfs(i: int) -> None:
            d = k - len(path)  # 还要选 d 个数
            if d == 0:
                ans.append(path.copy())
                return
            # 不选 i
            if i > d: dfs(i - 1)
            # 选 i
            path.append(i)
            dfs(i - 1)
            path.pop()
        dfs(n)
        return ans

法二:枚举下一个数选哪个

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans = []
        path = []
        def dfs(i: int) -> None:
            d = k - len(path)  # 还要选 d 个数
            if d == 0:
                ans.append(path.copy())
                return
            for j in range(i, d - 1, -1):
                path.append(j)
                dfs(j - 1)
                path.pop()
        dfs(n)
        return ans

算法练习一:LeetCode216. 组合总和 III

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:只使用数字1到9,每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
在这里插入图片描述

此题可以在上一题的代码基础上修改,上一题已将所有选择k个数的集合返回,故本题只需在代码返回时判断当前组合之和是否为n,并且若总和大于n,即可直接退出递归进行剪枝

法一:选或不选

func combinationSum3(k int, n int) (ans [][]int){
    path := []int{}
    var dfs func(int)
    dfs = func(i int) {
        d := k - len(path) // 还要选 d 个数
        if d == 0 && sum(path)==n{
            ans = append(ans, append([]int(nil), path...))
            return
        }else if d<0||sum(path)>n{
            return
        }
        // 不选 i
        if i > d {
            dfs(i - 1)
        }
        // 选 i
        path = append(path, i)
        dfs(i - 1)
        path = path[:len(path)-1]
    }
    dfs(9)
    return
}
func sum(nums []int)int{
    ans:=0
    for _,num:=range nums{
        ans+=num
    }
    return ans
}

法二:枚举下一个数选哪个

func combinationSum3(k int, n int) (ans [][]int){
    path := []int{}
    var dfs func(int)
    dfs = func(i int) {
        d := k - len(path) // 还要选 d 个数
        if d == 0 &&sum(path)==n{
            ans = append(ans, append([]int(nil), path...))
            return
        }else if d<0||sum(path)>n{
            return
        }
        for j := i; j >= d; j-- {
            path = append(path, j)
            dfs(j - 1)
            path = path[:len(path)-1]
        }
    }
    dfs(9)
    return
}
func sum(nums []int)int{
    ans:=0
    for _,num:=range nums{
        ans+=num
    }
    return ans
}

算法练习二:LeetCode22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。在这里插入图片描述

此题我们可以选择选或不选的思路,选择即是左括号,不选则表示为右括号,且分别用left与right记录当前生成的左括号与右括号个数,如果right>left,那么说明右括号比左括号还多,不符合要求,故直接返回。用m=2*n来记录当前长度,只有当递归到m时即可添加到ans中。对于选或不选直接记录,递归,回溯三步完成。

func generateParenthesis(n int) (ans []string) {
    cur:=[]byte{}
    m:=2*n
    left,right:=0,0
    var dfs func(int)
    dfs=func(i int){
        if i==m{
            ans=append(ans,string(cur))
            return
        }
        if right>left{
            return
        }
        //选左括号
        if left<n{
        left++
        cur=append(cur,'(')
        dfs(i+1)
        cur=cur[:len(cur)-1]
        left--
        }
        //选右括号
        right++
        cur=append(cur,')')
        dfs(i+1)
        cur=cur[:len(cur)-1]
        right--
    }
    dfs(0)
    return 
}

算法练习三:LeetCode301. 删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。返回所有可能的结果。答案可以按 任意顺序 返回。1 <= s.length <= 25,s 由小写英文字母以及括号 ‘(’ 和 ‘)’ 组成,s 中至多含 20 个括号
在这里插入图片描述

本题题目是删除无效的括号,但可以改变思路,与上题一样,可以考虑从字符串s中选择字符以满足括号有效,同样使用left与right记录当前选择的左括号与右括号个数,用letter记录字母的个数,且因为要删除最小数量的无效括号,可以考虑将所有有效的括号用ans记录,并利用maxLen记录满足条件的最长字符串,这样就之后删除最少的无效括号,之后使用函数findUniqueStrings来找出记录的所有满足条件的ans中长度为maxLen的字符串,且消除重复的字符串。

func removeInvalidParentheses(s string) (ans []string) {
    left,right,letter:=0,0,0
    cur:=[]byte{}
    maxLen:=-1
    n:=len(s)
    var dfs func(i int)
    dfs=func(i int){
        if i==n&&right==left{
            ans=append(ans,string(cur))
            //记录删除最少的括号后的长度
            maxLen=max(maxLen,left+letter+right)
            return
        }else if i==n||right>left{
            return
        }
        if s[i]=='('{
            //左括号不添加
            dfs(i+1)
            //左括号添加
            left++
            cur=append(cur,'(')
            dfs(i+1)
            cur=cur[:len(cur)-1]
            left--
            
        }else if s[i]==')'{
            if(left>right){
            //右括号添加
            right++
            cur=append(cur,')')
            dfs(i+1)
            cur=cur[:len(cur)-1]
            right--
            }
            //右括号不添加
            dfs(i+1)
        }else{
        	//添加字母
            cur=append(cur,s[i])
            letter++
            dfs(i+1)
            letter--
            cur=cur[:len(cur)-1]
        }
    }
    dfs(0)
    if len(ans)==0{
        return []string{""}
    }
    return findUniqueStrings(ans,maxLen)
}
//找出最长的字符串,即删除最小括号,且去掉重复字符串
func findUniqueStrings(s []string, n int) []string {
    uniqueMap := make(map[string]bool)
    for i := 0; i < len(s); i++ {
        if len(s[i]) == n {
            uniqueMap[s[i]] = true
        }
    }
    uniqueStrings := make([]string, 0, len(uniqueMap))
    for unique := range uniqueMap {
        uniqueStrings = append(uniqueStrings, unique)
    }
    return uniqueStrings
}
func max(a,b int)int{if a>b{return a};return b}

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

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

相关文章

【C++ 十八】C++ map/ multimap容器

C map/ multimap 容器 文章目录 C map/ multimap 容器前言1 map 基本概念2 map 构造和赋值3 map 大小和交换4 map 插入和删除5 map 查找和统计6 map 容器排序 总结 前言 本文包含map基本概念、map构造和赋值、map大小和交换、map插入和删除、map查找和统计、map容器排序。 1 m…

使用Glib中测试框架对C代码进行单元测试

C项目的测试框架比较常见的是Google的gtest&#xff08;前文CMake项目使用ctestgtest进行单元测试有使用实例介绍gtest&#xff0c;感兴趣的读者可以去看看&#xff09;&#xff0c;也有一些其它框架&#xff0c;比如Boost中的测试框架。这些框架虽然也可以测试C代码&#xff0…

Vue 消息订阅与发布

消息订阅与发布&#xff0c;也可以实现任意组件之间的通信。 订阅者&#xff1a;就相当于是我们&#xff0c;用于接收数据。 发布者&#xff1a;就相当于是媒体&#xff0c;用于传递数据。 安装消息订阅与发布插件&#xff1a; 在原生 JS 中 不太容易实现消息订阅与发布&…

Unity-ML-Agents-代码解读-RollerBall

使用版本&#xff1a;https://github.com/Unity-Technologies/ml-agents/releases/tag/release_19 文件路径&#xff1a;ml-agents-release_19/docs/Learning-Environment-Create-New.md 20和19的在rollerBall上一样&#xff1a;https://github.com/Unity-Technologies/ml-ag…

CSDN博客编写教程

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

osg widget 试用 方法

按钮 一个常见的 osg::Widget 就是按钮。下面的代码展示了如何使用 osg::Switch 和 osgText 创建一个简单的按钮&#xff1a; osg::ref_ptr<osg::Switch> buttonSwitch new osg::Switch(); osg::ref_ptr<osgText::Text> buttonText new osgText::Text(); buttonT…

浏览器不好用?插件来帮忙

一、目的 浏览器本身具备的功能并不完善&#xff0c;不同的用户可以为自己浏览器增加想要功能&#xff0c;使得浏览器更能符合自己的需求&#xff0c;提高浏览器使用的舒适度 二、推荐插件 AdblockPlus LastPass&#xff08;密码记录&#xff0c;全平台通用&#xff09; Dar…

JSON的用法和说明

JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式。 JSON建构于两种结构&#xff1a; "名称/值"对的集合。理解为对象 值的有序列表。理解为数组 JSON具有以下这些形式&#xff1a; 对象是一个无序的“ ’名称/值‘ 对”集合。一个…

阿里 Arthas (阿尔萨斯)工具的使用

目录 使用 一、安装与启动命令行控制台使用 使用 这款工具可以监控线上、测试或者其他环境的java运行中程序的情况&#xff0c;用于定位线上、测试等环境的问题。 一、安装与启动 通过termius远程登录测试或者线上环境&#xff0c;cd到指定目录下&#xff0c;输入命令&#…

高效部署Redis Sentinel模式(哨兵模式),手把手教学

Redis Sentinel模式部署 前言一、服务器部署同版本的redis1、换软件源在yum拉取包的时候启用remi源 二、修改配置文件1.修改/etc/redis.conf2.配置/etc/redis/sentinel.conf 三、启动redis服务1、启动服务2、连接redis3、检查redis 前言 这里就不过多的解释高可用的好处了&…

设计模式:行为型模式 - 迭代器模式

文章目录 1.概述2.结构3.案例实现4.优缺点5.使用场景6.JDK源码解析 1.概述 定义&#xff1a; 提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。 2.结构 迭代器模式主要包含以下角色&#xff1a; 抽象聚合&#xff08;Aggregate&…

Learn OpenCV by Examples - with Python

目录 关于OpenCV 新增内容 Content 1.锐化 2.阈值&#xff0c;二值化和自适应阈值 本文是自己在kaggle上学习OpenCV的学习笔记&#xff0c;如果对你有所帮助再好不过了。这是原文链接Learn OpenCV by Examples - with Python | Kaggle里面不仅有代码还有图片等。如果你还没…

网络安全-CDN绕过寻找真实IP

网络安全-CDN绕过寻找真实IP CDN就是CDN加速&#xff0c;就是根据你的目标让你访问的更快 CDN CDN&#xff0c;即内容分发网络&#xff0c;主要解决因传输距离和不同运营商节点造成的网络速度性能低下的问题。说得简单点&#xff0c;就是一组在不同运营商之间的对接节点上的…

Docker AIGC等大模型深度学习环境搭建(完整详细版)

本文是《Python从零开始进行AIGC大模型训练与推理》&#xff08;https://blog.csdn.net/suiyingy/article/details/130169592&#xff09;专栏的一部分&#xff0c;所述方法和步骤基本上是通用的&#xff0c;不局限于AIGC大模型深度学习环境。 Docker AIGC等大模型深度学习环境…

Go语言之反射(反射的简单使用,原理)

一、反射的基础 1.什么是反射 Go语言中&#xff0c;反射的机制就是在运行的时候&#xff0c;可以获取到其变量的类型和值&#xff0c;且可以对其类型和值进行检查&#xff0c;对其值进行修改。即在不知道具体的类型的情况下&#xff0c;可以用反射机制来查看变量类型、更新变…

50 Projects 50 Days - Hidden Search Widget 学习记录

项目地址 Hidden Search Widget 展示效果 Hidden Search Widget 实现思路 点击搜索按钮&#xff0c;展开输入框&#xff0c;主要元素就两个&#xff1a;input输入框和button&#xff0c;这两个本身就是行内元素。点击触发的动作拆分为两个&#xff0c;第一个是input输入框…

Vue核心 事件处理

1.8. 事件处理 1.8.1.事件的基本使用: 使用v-on:xxx或**xxx**绑定事件&#xff0c;其中 xxx 是事件名事件的回调需要配置在methods对象中&#xff0c;最终会在vm上methods中配置的函数&#xff0c;不要用箭头函数&#xff0c;否则this就不是vm了methods中配置的函数&#xff…

手撕Twitter推荐算法

Twitter近期开源了其推荐系统源码[1,2,3]&#xff0c;截止现在已经接近36k star。但网上公开的文章都是blog[1]直译&#xff0c;很拗口&#xff0c;因此特地开个系列系统分享下。系列涵盖&#xff1a; Twitter整体推荐系统架构&#xff1a;涵盖图数据挖掘、召回、精排、规则多…

ActiveMQ使用

一、什么是消息中间件 消息中间件顾名思义实现的就是在两个系统或两个客户端之间进行消息传送 二、什么是ActiveMQ ActiveMQ是一种开源的基于JMS&#xff08;Java Message Servie&#xff09;规范的一种消息中间件的实现&#xff0c;ActiveMQ的设计目标是提供标准的&#xff0c…

HCIP之VLAN

目录 网络的三层架构 接入层 无线的缺陷&#xff1a; 上网用户数量增多&#xff0c;网络卡顿的原因 CSMA/CD --- 载波侦听多路访问/冲突检测 CSMA/CA --- 载波侦听多路访问/冲突避免 无线网络没有使用冲突检测技术的原因 汇聚层 连接两条线路的原因 核心层 VLAN VLAN配…