当前位置:首页 > 文章列表 > Golang > Go问答 > Go llrb 升序遍历与降序遍历的边界有什么区别

Go llrb 升序遍历与降序遍历的边界有什么区别

来源:17golang原创 2026-10-31 10:35:46 0浏览 收藏

Go llrb 的升序和降序遍历,关键不在方法名里的 Ascend 或 Descend,而在边界是否包含 pivot。需要“从某个值开始,包含它本身”时,用 AscendGreaterOrEqual;需要“只取小于某个值”时,用 AscendLessThan;需要一个范围时,AscendRange 表示下界包含、上界排除。反向读取时,DescendLessOrEqual 会按降序遍历不大于 pivot 的元素。

最容易写错的是把“升序/降序”当成唯一判断条件。实际选择要同时看方向、pivot 是否包含、是否需要上下界,以及回调返回 false 后是否应该提前停止。
  • 升序起点:AscendGreaterOrEqual 包含 pivot,AscendLessThan 排除 pivot。
  • 升序范围:AscendRange(lower, upper, iterator) 采用大于等于 lower 且小于 upper 的闭开区间。
  • 降序边界:DescendLessOrEqual 只处理不大于 pivot 的元素,并从大到小回调。

先固定 llrb 的比较顺序和回调模型

GoLLRB/llrb 是基于左倾红黑树的有序树实现。元素需要满足 Item 接口,比较关系由 Less(than Item) bool 决定。因此,本文里的“大于”“小于”和“升序”都不是对某个字段硬编码,而是遵循你为 Item 定义的 Less 规则。

遍历方法接收一个 ItemIterator 回调。回调返回 true 表示继续,返回 false 表示停止继续遍历。它只是遍历控制信号,不会改变树里的元素,也不代表某个元素已经被删除。

type ItemIterator func(i Item) bool

// 这个示例只展示比较规则和回调意图,不把打印顺序当作接口契约。
type Score int

func (s Score) Less(than Item) bool {
	// 只有同一类型的分数才参与比较,调用方应保证数据类型一致。
	return s 

这里的 visit 只是说明回调形态。真正使用时,要把类型断言、重复键处理和停止条件设计好,尤其不要把“回调停止”误读成“边界自动包含”。边界由具体方法的契约决定。

两个升序方法到底差在哪里

升序遍历经常从两个看起来相近的方法里选错。AscendGreaterOrEqual(pivot, iterator) 会访问大于等于 pivot 的元素,pivot 如果存在,会被包含;AscendLessThan(pivot, iterator) 会访问小于 pivot 的元素,pivot 即使存在,也会被排除。

我在把“从某个分数开始展示”改成“展示某个分数以前的记录”时,最容易忽略的就是这个方向变化:前者是右侧集合并包含起点,后者是左侧集合并排除终点。方法名里的 GreaterOrEqual 和 LessThan 才是边界契约。

Go llrb 三个升序遍历方法的 pivot 包含关系、闭开区间和回调出口说明图
图1:升序遍历边界说明图,查看 pivot 包含关系与 AscendRange 的闭开区间;这是静态说明图,不是运行截图。

可以用下面的选择方式记忆:

目标集合方法边界
从 pivot 向上取AscendGreaterOrEqualpivot 包含
取 pivot 以前的元素AscendLessThanpivot 排除
取一个升序范围AscendRange下界包含,上界排除

用 AscendRange 表达闭开区间

AscendRange(greaterOrEqual, lessThan, iterator) 适合把两个单侧条件合并成一个范围:元素必须大于等于第一个参数,同时小于第二个参数。也就是常见的 [lower, upper),下界进入结果,上界不进入结果。

tree.AscendRange(Score(60), Score(90), func(i Item) bool {
	// 这里接收到的是 [60, 90) 的静态范围语义,90 不属于结果。
	score := i.(Score)
	// 业务处理应放在回调里;返回 false 可在满足条件时停止。
	return score != 80
})

如果 lower 与 upper 相同,闭开区间没有可取元素;如果 lower 大于 upper,也不能把它当成“自动倒序范围”。需要倒序读取时应改用降序方法或重新表达边界,不要期待 AscendRange 帮你翻转参数含义。

降序遍历为什么只写一个 pivot

DescendLessOrEqual(pivot, iterator) 的语义是访问小于等于 pivot 的元素,并且按降序回调。它不是把升序方法的结果倒放,而是直接用一个上边界限定左侧集合:pivot 存在时会包含 pivot,然后依次向更小的元素走。

Go llrb DescendLessOrEqual 的不大于 pivot 集合、降序方向和 ItemIterator 停止关系说明图
图2:降序遍历结构说明图,查看不大于 pivot 的元素集合与回调停止关系;这是静态说明图,不是运行截图。
tree.DescendLessOrEqual(Score(90), func(i Item) bool {
	// 回调顺序是从不大于 90 的较大值向较小值移动。
	score := i.(Score)
	// 业务只要看到 70,就可以停止,不必继续访问更小的元素。
	return score != 70
})

降序方法只有一个 pivot,并不表示它支持任意双边区间。若需要“从 90 向下,但不低于 60”,可以在回调中遇到 60 之后返回 false;但这只是利用有序回调做提前停止,边界本身仍要由你的条件清楚表达。

把方法选择收敛成工程判断

先问四个问题,再写方法名:想要升序还是降序?pivot 要不要包含?是否同时存在下界和上界?回调遇到业务终点后要不要停止?这四个问题比背 API 名字更可靠。

需求选择需要记住的边界
从起点向更大值遍历AscendGreaterOrEqual起点包含
向上遍历到终点之前AscendLessThan终点排除
固定升序区间AscendRange[lower, upper)
从上限向更小值倒序取DescendLessOrEqual上限包含

还有两个实践提醒。第一,重复键是否允许取决于插入方法和 Item 的排序关系,不能用“遍历一次”推断每个业务 ID 只出现一次。第二,回调返回 false 只控制这次遍历是否继续,不会把树改成另一种状态。如果需要分页或游标,要把最后一个 Item 的边界语义单独记录下来。

Go llrb 遍历边界常见问题

AscendRange 的上界会被包含吗?

不会。官方方法契约使用大于等于下界且小于上界的语义,因此它是闭开区间 [lower, upper)。若业务需要包含 upper,需要重新设置范围或在业务层处理边界。

DescendLessOrEqual 会返回大于 pivot 的元素吗?

不会。它只遍历小于等于 pivot 的元素,并按降序回调。需要 pivot 以上的集合时,应回到升序方法或重新定义查询方向。

回调返回 false 是不是等于删除当前元素?

不是。返回 false 只是停止继续遍历,不改变树中的元素。删除应使用树提供的删除方法,并单独考虑遍历期间修改结构的风险。

Go llrb 的遍历选择可以压缩成一句话:先看比较方向,再看边界包含,最后看回调停止。记住 AscendGreaterOrEqual 包含 pivot、AscendLessThan 排除 pivot、AscendRange 使用闭开区间,以及 DescendLessOrEqual 按降序包含上限,就能避开大多数“结果多一条或少一条”的问题。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go llrb ReplaceOrInsert怎样判断两个对象相同Go llrb ReplaceOrInsert怎样判断两个对象相同
上一篇
Go llrb ReplaceOrInsert怎样判断两个对象相同
Go llrb 插入后修改排序字段为什么会破坏检索
下一篇
Go llrb 插入后修改排序字段为什么会破坏检索
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    809次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    798次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    890次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    861次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    687次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码