Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Go语言中如何优雅地泛化不相交集(DisjointSets)数据结构_创想鸟

Go语言中如何优雅地泛化不相交集(DisjointSets)数据结构

go语言中如何优雅地泛化不相交集(disjointsets)数据结构

本文探讨了如何利用Go语言的`interface{}`机制,将一个最初为`int64`类型设计的DisjointSets(不相交集)数据结构泛型化,使其能够支持`float64`、`string`等多种类型。通过将元素类型抽象为`interface{}`,并利用Go语言中map键必须可比较的特性,我们能够以最小的代码改动实现数据结构的通用性,避免为每种新类型编写重复实现。

理解不相交集(DisjointSets)数据结构

不相交集(DisjointSets),又称并查集(Union-Find Set),是一种用于处理一组不相交的动态集合的数据结构。它支持两种主要操作:

FindSet(x):查找元素x所属的集合的代表元素(通常是根节点)。Union(x, y):将包含元素x和y的两个集合合并为一个集合。

其核心实现通常包括:

p (parent):一个映射,记录每个元素的父节点。ranks (rank):一个映射,记录每个集合的“秩”或“高度”,用于在合并时优化树的结构(路径压缩和按秩合并)。

以下是一个基于int64类型实现的Go语言不相交集数据结构示例:

立即学习“go语言免费学习笔记(深入)”;

package mainimport "fmt"// DisjointSets 结构体定义,目前仅支持 int64 类型type DisjointSets struct {    ranks map[int64]int64    p map[int64]int64}// NewDisjointSets 创建并返回一个新的 DisjointSets 实例func NewDisjointSets() *DisjointSets {    d := DisjointSets{map[int64]int64{}, map[int64]int64{}}    return &d}// MakeSet 将元素 x 添加到不相交集中,作为其自身集合的代表func (d *DisjointSets) MakeSet(x int64) {    d.p[x] = x    d.ranks[x] = 0}// Link 根据秩(rank)合并两个根节点 x 和 yfunc (d *DisjointSets) Link(x, y int64) {    if d.ranks[x] > d.ranks[y] {        d.p[y] = x    } else {        d.p[x] = y        if d.ranks[x] == d.ranks[y] {            d.ranks[y] += 1        }    }}// FindSet 查找元素 x 所属集合的代表元素,并进行路径压缩func (d *DisjointSets) FindSet(x int64) int64 {    if x != d.p[x] {        d.p[x] = d.FindSet(d.p[x]) // 路径压缩    }    return d.p[x]}// Union 合并包含元素 x 和 y 的两个集合func (d *DisjointSets) Union(x, y int64) {    d.Link(d.FindSet(x), d.FindSet(y))}

上述实现的问题在于,它被硬编码为只处理int64类型。如果我们需要处理float64、string或其他自定义类型,就必须复制并修改整个数据结构,这显然不是一个高效或优雅的解决方案。

Go语言的泛型之道:interface{}

在Go语言中,实现泛型的一种常见且强大的方式是使用空接口interface{}。interface{}可以表示任何类型的值。当我们需要一个数据结构能够存储和操作多种不同类型的值时,interface{}提供了一种灵活的抽象。

对于DisjointSets数据结构,其核心操作(MakeSet、FindSet、Union)主要依赖于元素的相等性比较以及作为map的键。Go语言规定,所有可比较的类型(如数值类型、字符串、布尔值、指针、通道、结构体(如果所有字段都可比较)、数组(如果所有元素都可比较))都可以作为map的键。interface{}类型的值如果其底层类型是可比较的,那么它也可以作为map的键。这为我们泛型化DisjointSets提供了基础。

泛型化 DisjointSets 的实现

要将DisjointSets泛型化,我们只需将结构体中map的键类型以及所有方法签名中的元素类型从int64改为interface{}。

package mainimport "fmt"// DisjointSets 泛型化后的结构体定义,支持任意可比较类型type DisjointSets struct {    ranks map[interface{}]int64 // rank值仍为 int64    p map[interface{}]interface{} // 父节点现在可以是任意类型}// NewDisjointSets 创建并返回一个新的泛型 DisjointSets 实例func NewDisjointSets() *DisjointSets {    d := DisjointSets{map[interface{}]int64{}, map[interface{}]interface{}{}}    return &d}// MakeSet 将元素 x 添加到不相交集中func (d *DisjointSets) MakeSet(x interface{}) {    // 确保 x 是可比较的,作为 map 的键    d.p[x] = x    d.ranks[x] = 0}// Link 根据秩合并两个根节点 x 和 yfunc (d *DisjointSets) Link(x, y interface{}) {    // x 和 y 必须是 FindSet 返回的根节点    if d.ranks[x] > d.ranks[y] {        d.p[y] = x    } else {        d.p[x] = y        if d.ranks[x] == d.ranks[y] {            d.ranks[y] += 1        }    }}// FindSet 查找元素 x 所属集合的代表元素,并进行路径压缩func (d *DisjointSets) FindSet(x interface{}) interface{} {    // 检查 x 是否已存在于集合中,若不存在则无法查找    if _, ok := d.p[x]; !ok {        // 可以选择在这里抛出错误或 MakeSet(x)        // 为了教程简洁,假设调用前已 MakeSet        return nil // 或者 panic("element not found")    }    if x != d.p[x] {        d.p[x] = d.FindSet(d.p[x]) // 路径压缩    }    return d.p[x]}// Union 合并包含元素 x 和 y 的两个集合func (d *DisjointSets) Union(x, y interface{}) {    // 调用前需确保 x 和 y 均已 MakeSet    rootX := d.FindSet(x)    rootY := d.FindSet(y)    if rootX != nil && rootY != nil && rootX != rootY {        d.Link(rootX, rootY)    }}func main() {    // 示例使用:处理 int 类型    dsInt := NewDisjointSets()    dsInt.MakeSet(1)    dsInt.MakeSet(2)    dsInt.MakeSet(3)    dsInt.MakeSet(4)    dsInt.Union(1, 2)    dsInt.Union(3, 4)    dsInt.Union(2, 3)    fmt.Printf("FindSet(1): %vn", dsInt.FindSet(1)) // 预期为 1 或 4    fmt.Printf("FindSet(2): %vn", dsInt.FindSet(2))    fmt.Printf("FindSet(3): %vn", dsInt.FindSet(3))    fmt.Printf("FindSet(4): %vn", dsInt.FindSet(4))    fmt.Println("---")    // 示例使用:处理 string 类型    dsString := NewDisjointSets()    dsString.MakeSet("apple")    dsString.MakeSet("banana")    dsString.MakeSet("cherry")    dsString.MakeSet("date")    dsString.Union("apple", "banana")    dsString.Union("cherry", "date")    dsString.Union("banana", "cherry")    fmt.Printf("FindSet("apple"): %vn", dsString.FindSet("apple")) // 预期为 "apple" 或 "date"    fmt.Printf("FindSet("banana"): %vn", dsString.FindSet("banana"))    fmt.Printf("FindSet("cherry"): %vn", dsString.FindSet("cherry"))    fmt.Printf("FindSet("date"): %vn", dsString.FindSet("date"))    fmt.Println("---")    // 示例使用:处理 float64 类型    dsFloat := NewDisjointSets()    dsFloat.MakeSet(1.1)    dsFloat.MakeSet(2.2)    dsFloat.MakeSet(3.3)    dsFloat.MakeSet(4.4)    dsFloat.Union(1.1, 2.2)    dsFloat.Union(3.3, 4.4)    dsFloat.Union(2.2, 3.3)    fmt.Printf("FindSet(1.1): %vn", dsFloat.FindSet(1.1)) // 预期为 1.1 或 4.4    fmt.Printf("FindSet(2.2): %vn", dsFloat.FindSet(2.2))    fmt.Printf("FindSet(3.3): %vn", dsFloat.FindSet(3.3))    fmt.Printf("FindSet(4.4): %vn", dsFloat.FindSet(4.4))}

使用与注意事项

Map键的可比较性:这是使用interface{}实现泛型的关键。作为map键的interface{}值,其底层类型必须是可比较的。Go语言中,基本类型(int, string, bool, float等)、指针、通道、结构体(所有字段可比较)、数组(所有元素可比较)都是可比较的。切片(slice)、映射(map)和函数(func)是不可比较的,因此不能直接作为map的键。如果尝试使用不可比较的类型作为键,Go运行时会发生panic。类型断言:在本DisjointSets的例子中,我们只需要比较元素是否相等,这由interface{}的底层值比较自动处理。如果你的泛型数据结构需要对interface{}中的具体类型执行特定操作(例如,对int进行加法,对string进行拼接),你就需要使用类型断言(value.(type)或value.(SpecificType))来获取底层类型并进行操作。但对于DisjointSets,这并非必需。性能考虑:使用interface{}会引入一定的运行时开销,因为interface{}值在内部由两部分组成:类型信息和值数据。每次赋值或比较都可能涉及额外的间接寻址。对于性能极度敏感的场景,或者在Go 1.18+版本中,可以考虑使用Go原生的泛型(Type Parameters)来获得更好的类型安全和潜在的性能优势。然而,对于大多数通用数据结构而言,interface{}的开销通常在可接受范围内。错误处理:在FindSet方法中,如果尝试查找一个从未通过MakeSet添加的元素,d.p[x]将返回零值。在实际应用中,你可能需要更健壮的错误处理,例如返回一个错误或在MakeSet中预先检查元素是否存在。

总结

通过将DisjointSets数据结构中的元素类型从具体的int64替换为interface{},我们成功地将其泛型化,使其能够处理int、string、float64等多种可比较的类型,而无需为每种类型重复编写代码。这种方法是Go语言在引入原生泛型之前实现通用数据结构的常见模式。理解interface{}的工作原理以及Go中map键的可比较性是实现这一目标的关键。在Go 1.18及更高版本中,Go原生泛型提供了更类型安全和编译时检查的泛型实现方式,但interface{}作为一种灵活的运行时多态机制,在许多场景下仍然非常有用。

以上就是Go语言中如何优雅地泛化不相交集(DisjointSets)数据结构的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1418332.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Go语言结构体标签详解:以XML编码为例
上一篇 2025年12月16日 12:11:37
如何在Golang中实现中介者模式
下一篇 2025年12月16日 12:11:50

相关推荐

  • VSCode 怎样设置编辑器的字体连写效果 VSCode 字体连写效果的创意设置教程​

    要让vscode支持字体连写,需先安装支持连写的字体如fira code,再在settings.json中配置”editor.fontfamily”并将”editor.fontligatures”设为true,最后重启vscode验证效果;若不生效,检…

    2026年9月25日
    1200
  • 使用 Jackson 进行复杂类的自定义反序列化

    使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化

    本文介绍了如何使用 Jackson 库对包含复杂嵌套类的 JSON 字符串进行自定义反序列化。通过 ObjectMapper 的 readValue 方法可以实现简单场景下的自动反序列化。针对需要定制化处理的场景,可以结合 ObjectMapper 和自定义反序列化器来实现更灵活的反序列化逻辑,并提…

    2026年9月25日 • 用户投稿
    900
  • sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明

    sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明

    命令面板是Sublime Text高效操作核心,通过Ctrl+Shift+P(Win/Linux)或Cmd+Shift+P(macOS)打开,输入关键词如theme、syntax、package可快速执行更换主题、设置语法、安装插件等命令,支持动态搜索与回车执行,结合常用命令如设置修改、快捷键调整、…

    2026年9月25日 • 用户投稿
    400
  • 天猫行业标准有哪些?如何分析行业数据?详解天猫四大行业标准体系!

    天猫行业标准有哪些?如何分析行业数据?详解天猫四大行业标准体系!天猫行业标准有哪些?如何分析行业数据?详解天猫四大行业标准体系!天猫行业标准有哪些?如何分析行业数据?详解天猫四大行业标准体系!天猫行业标准有哪些?如何分析行业数据?详解天猫四大行业标准体系!

    在天猫这个日活破亿的电商主战场中,行业规范是入场门槛,数据运营则是突围关键。目前天猫已构建起覆盖商品品质、服务响应、营销合规与物流履约的四大标准框架,并依托直通车、生意参谋等工具打造了全链路的数据分析体系。本文将深入解读天猫核心规则,并结合真实案例展示如何借助多维数据提升店铺竞争力。 一、天猫四大核…

    2026年9月25日 • 用户投稿
    700
  • 如何通过Golang日志诊断Debian网络问题

    如何通过Golang日志诊断Debian网络问题如何通过Golang日志诊断Debian网络问题如何通过Golang日志诊断Debian网络问题如何通过Golang日志诊断Debian网络问题

    本文介绍如何利用Golang日志机制在Debian系统中高效诊断网络问题。我们将探讨几种实用方法,帮助您快速定位并解决网络连接故障。 一、日志记录 标准库log包: Golang的log包是记录网络请求和响应细节的理想选择。 在发送请求前后添加日志,可以清晰地追踪请求的发送和接收过程。以下是一个简单…

    2026年9月25日 • 用户投稿
    000
  • 灵通资讯字体大小设置方法

    灵通资讯字体大小设置方法灵通资讯字体大小设置方法灵通资讯字体大小设置方法灵通资讯字体大小设置方法

    如何在灵通资讯app中调整字体大小?本文将为您一步步讲解具体操作流程,帮助您快速完成设置,优化阅读舒适度。 1、 启动灵通资讯App后,在首页左上角找到并点击头像图标。 2、 跳转至个人页面后,点击右上角的“设置”图标进入系统选项。 3、 在设置菜单中,选择“字体大小”这一项。 搜狐资讯 AI资讯助…

    2026年9月25日 • 用户投稿
    100
  • 松下携全场景智慧生活方案亮相第四届数贸会旗舰洗护新品首秀

    松下携全场景智慧生活方案亮相第四届数贸会旗舰洗护新品首秀松下携全场景智慧生活方案亮相第四届数贸会旗舰洗护新品首秀松下携全场景智慧生活方案亮相第四届数贸会旗舰洗护新品首秀松下携全场景智慧生活方案亮相第四届数贸会旗舰洗护新品首秀

    第四届全球数字贸易博览会(以下简称“数贸会”)于2025年9月25日在杭州大会展中心隆重启幕。松下电器以“百年匠心 智慧怡居”为主题,携全系列住空间家电产品及创新互动体验登陆8号馆智慧空间展区,通过场景化展陈展示数字技术驱动下的高品质生活解决方案,并联动松下商城打造多元互动模式,推动数字贸易与消费体…

    2026年9月25日 • 用户投稿
    200
  • 豆包AI如何调用外部API 实现AI与第三方服务联动的方法

    本文旨在探讨豆包AI如何通过调用外部API,从而实现与第三方服务的智能联动。我们将详细介绍实现这一功能的核心原理以及具体的操作步骤。通过理解API调用的机制并在豆包AI中进行相应的配置,用户可以赋予豆包AI连接互联网世界、获取实时信息、执行特定任务的能力,极大地扩展了AI的应用场景和智能化水平。文章…

    2026年9月25日
    000
  • 动态缓存键配置:Spring Boot 缓存管理的灵活应用

    动态缓存键配置:Spring Boot 缓存管理的灵活应用动态缓存键配置:Spring Boot 缓存管理的灵活应用动态缓存键配置:Spring Boot 缓存管理的灵活应用动态缓存键配置:Spring Boot 缓存管理的灵活应用

    在 Spring Boot 应用中,使用 @Cacheable 注解可以方便地实现缓存功能。然而,在某些场景下,我们需要根据请求参数动态地生成缓存键,而不是简单地使用固定的键值。虽然 @Cacheable 注解允许通过 key 属性指定 SpEL 表达式来生成缓存键,但有时我们可能需要更灵活的控制,…

    2026年9月25日 • 用户投稿
    200
  • 为什么不同浏览器对硬件加速的实现存在差异?

    不同浏览器因渲染引擎、图形API及权衡策略差异导致硬件加速表现不同。1. Blink、Gecko、WebKit引擎在图层管理与GPU任务分配上设计不同;2. 各浏览器通过ANGLE等抽象层适配DirectX、Vulkan、Metal,转换开销与支持程度影响性能;3. 厂商在性能、兼容性、稳定性间取舍…

    2026年9月25日
    100
  • 快手直播间怎么装修_快手直播间装修的实用方法与建议

    快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议

    明确直播主题、优化灯光布局、设计简洁背景墙、合理规划功能区及改善声网环境是提升快手直播间专业度的关键。首先根据内容类型确定风格,如美妆选柔和色调,游戏用科技感灯光;参考热门主播布置并保持视觉统一。主光源采用4500K环形灯,辅以侧补光和背景灯带增强层次。背景选用低饱和纯色墙,搭配品牌LOGO或绿植,…

    2026年9月25日 • 用户投稿
    100
  • 这台五万元的相机,哈苏想卖给「普通人」

    这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」

    拍照,可能是这个时代门槛最低的创作行为了。 我们每天都在生产和消费着海量的图片,记录变得前所未有地容易,但容易,就等于好吗? 过去,哈苏的答案是倾向于「好」,但代价是「难」——你需要理解光圈、快门,要背着沉重的三脚架,甚至要在特定的拍摄环境中,才能驾驭这份极致的画质。 在推出了备受瞩目的 X2D 1…

    2026年9月25日 • 用户投稿
    200
  • C语言判断素数方法

    C语言判断素数方法C语言判断素数方法C语言判断素数方法C语言判断素数方法

    求素数的问题通常可以划分为两大类。 1、解决素数相关问题常用的方法主要有两种。 2、判断某个给定的数是否为质数。 3、找出所有小于指定数值的质数。 4、核心概念包括:素数是大于1且只能被1和其本身整除的自然数。要判断一个数是否为素数,可以通过尝试用从2到该数减1的所有整数去除它,若发现有能整除的因子…

    2026年9月25日 • 用户投稿
    700
  • 豆包是否可以本地部署 自主可控环境下运行豆包的技术路径说明

    本文旨在解答关于豆包是否可以在本地环境下进行部署并实现自主可控运行的问题。目前,豆包主要以云服务形式提供,用户通过网络访问其功能。要在自主可控的环境下运行类似的大型语言模型能力,通常需要采用不同的技术路径,即在本地计算资源上部署可用的AI模型。本文将概述实现本地自主可控AI运行的通用技术路线和关键步…

    2026年9月25日
    300
  • 荣耀 300 系列系统升级,后续多款新机待发

    荣耀 300 系列系统升级,后续多款新机待发荣耀 300 系列系统升级,后续多款新机待发荣耀 300 系列系统升级,后续多款新机待发荣耀 300 系列系统升级,后续多款新机待发

    日前,荣耀 300 系列手机迎来 magicos 9.0.0.187 版本升级,此次更新带来了清理建议、ai 通话等多项新功能,系统升级将以分批推送的形式逐步覆盖用户。 本次更新的主要亮点如下: 图库方面新增“清理建议”功能,可智能识别重复照片、相似图片及超大视频,帮助用户更高效地管理存储空间; 通…

    2026年9月25日 • 用户投稿
    500
  • 动态缓存键在Spring Boot中的实现教程

    动态缓存键在Spring Boot中的实现教程动态缓存键在Spring Boot中的实现教程动态缓存键在Spring Boot中的实现教程动态缓存键在Spring Boot中的实现教程

    本文介绍了如何在Spring Boot应用中实现基于请求参数的动态缓存键。通过直接操作CacheManager获取缓存对象,并使用cache.get(key, () -> …)方法,可以灵活地根据请求参数生成缓存键,从而实现更精细化的缓存控制。这种方法避免了直接修改缓存名称,而是专…

    2026年9月25日 • 用户投稿
    700
  • sublime怎么设置字体和字号 _sublime字体与字号调整方法

    sublime怎么设置字体和字号 _sublime字体与字号调整方法sublime怎么设置字体和字号 _sublime字体与字号调整方法sublime怎么设置字体和字号 _sublime字体与字号调整方法sublime怎么设置字体和字号 _sublime字体与字号调整方法

    先修改用户设置文件以调整字体和字号,打开Preferences → Settings,在右侧User配置中添加”font_face”和”font_size”选项,如{“font_face”: “Fira Code&#…

    2026年9月25日 • 用户投稿
    000
  • Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    本文将详细介绍如何使用 Java 8 的 Stream API 将一个嵌套的 Map 结构进行扁平化处理,并从中提取所需的数据。 具体来说,我们将把 Map<Integer, Map<String, List>> 转换为 Map,其中新 Map 的键是原内部 Map 的键,值…

    2026年9月25日 • 用户投稿
    1200
  • 首个对话式音乐创作 Agent“Tunee”正式公测

    首个对话式音乐创作 Agent“Tunee”正式公测首个对话式音乐创作 Agent“Tunee”正式公测首个对话式音乐创作 Agent“Tunee”正式公测首个对话式音乐创作 Agent“Tunee”正式公测

    趣丸科技旗下天谱乐团队自主研发的国内首款对话式音乐创作agent“tunee”近日正式启动全球公测,全面向公众开放使用。 据悉,用户只需通过自然语言描述自己的音乐设想,即便表达模糊,Tunee也能自动完成需求解析、方案设计到实际作曲的完整流程,最终输出契合用户意图的原创音乐作品。 Tunee采用先进…

    2026年9月25日 • 用户投稿
    500
  • Debian syslog如何定制报警机制

    Debian syslog如何定制报警机制Debian syslog如何定制报警机制Debian syslog如何定制报警机制Debian syslog如何定制报警机制

    本文介绍如何在Debian系统中定制syslog报警机制,利用rsyslog实现更灵活的日志监控和告警。 首先,确保已安装rsyslog: sudo apt-get updatesudo apt-get install rsyslog 接下来,修改rsyslog配置文件,/etc/rsyslog.c…

    2026年9月25日 • 用户投稿
    100

发表回复

登录后才能评论
关注微信