news 2026/8/15 5:30:41

Drools WorkBench动态规则引擎:企业级业务规则实时更新实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Drools WorkBench动态规则引擎:企业级业务规则实时更新实战

1. 从静态到动态:为什么我们需要动态规则?

如果你做过几年企业级应用开发,尤其是风控、营销、计费这类业务,大概率会跟规则引擎打交道。传统的做法是把业务规则硬编码在代码里,或者写在配置文件里,每次规则变动,都需要开发人员修改代码、打包、测试、上线。这个过程,短则几小时,长则几天,业务人员只能干等着。我经历过最夸张的一次,一个紧急的营销活动规则调整,因为涉及多个系统联调,硬是拖了一周才上线,活动都快结束了。

这就是“静态规则”的痛点:变更成本高、响应慢、风险大。业务规则本应是业务专家(比如风控分析师、营销策划)的领域,却因为技术门槛被锁死在代码仓库里。而“动态规则”要解决的,正是这个核心矛盾。它允许业务人员在无需重启应用、无需开发介入的情况下,通过一个可视化的界面(比如Drools WorkBench)去创建、修改、发布规则,让规则真正“活”起来,能够实时响应市场变化。

Drools作为一款老牌且强大的开源规则引擎,其核心价值在于将业务逻辑与应用程序代码解耦。但仅仅使用Drools的API在代码中定义规则(DRL文件),这还只是第一步,我们称之为“开发时动态”。真正的“运行时动态”,需要配合Drools WorkBench(现在叫KIE WorkBench或Business Central)来实现。WorkBench提供了一个集中的、Web化的规则管理平台,它不仅仅是规则的“编辑器”,更是规则的“仓库”、“版本控制器”和“发布中心”。规则包(KJAR)可以从WorkBench直接发布到Maven仓库,然后被你的应用动态加载。

网络上热门的“aviator规则引擎”之所以被关注,正是因为它轻量、易集成,适合表达式级别的简单规则动态计算。但对于复杂的、多步骤的、需要推理的规则网络,Drools的DRL+WorkBench组合依然是企业级场景下的重型武器。至于“ansys workbench”、“mysql workbench”,那完全是不同领域的工具,只是名字巧合,不必混淆。我们聚焦的是Drools生态中的WorkBench。

2. WorkBench核心架构:远不止一个Web界面

很多人初次接触Drools WorkBench,会觉得它就是一个用来写DRL规则的网页版IDE。这个理解太表面了,也低估了它为实现“动态规则”所扮演的核心角色。我们可以把它拆解为四个关键层次来理解。

2.1 资源仓库与版本控制层

这是WorkBench的基石。它内置了Git仓库,你创建的所有项目、规则文件(.drl)、决策表(.xls/.xlsx)、数据模型(.java)、流程定义(.bpmn)等,都以文件的形式存储在这个Git仓库中。这意味着:

  • 完整的版本历史:每一次对规则的修改都有记录,可以轻松对比差异、回滚到任意版本。这对于审计和排查“某条规则是什么时候被谁改坏的”至关重要。
  • 分支与协作:团队可以创建特性分支进行规则开发,测试通过后再合并到主分支。业务分析师和开发人员可以基于同一套资源协作。
  • 与外部CI/CD集成:因为底层是Git,你可以通过hook将WorkBench中的提交事件通知到外部的Jenkins或GitLab CI,触发自动化的规则包构建和测试流程。

2.2 规则创作与建模层

这一层提供了业务友好的界面,降低编写规则的技术门槛。

  • 引导式规则编辑器:对于不熟悉DRL语法的人,可以通过表单填充的方式(When/Then)来创建规则,系统会自动生成背后的DRL代码。
  • 决策表:这是业务人员最喜爱的功能之一。规则可以以Excel表格的形式呈现,每一行代表一条规则,列是条件(Condition)和动作(Action)。调整费率、设置风控阈值,直接在表格里改数字就行,直观无比。
  • 数据对象模型:规则操作的对象(比如“用户”、“订单”、“交易”)需要被定义。WorkBench允许你上传JAR包或直接在其中定义Java类(虽然功能较简单),为规则提供“事实”(Fact)模型。
  • 测试场景:可以定义测试用例,模拟输入不同的事实数据,验证规则是否会输出预期的结果。这相当于为规则集编写单元测试,是保证规则质量的关键。

2.3 构建与部署层

规则编写好并测试通过后,需要打包成可部署的单元。

  • 项目与KJAR:在WorkBench中,规则是以“项目”为单位组织的。一个项目最终会被构建成一个KJAR(Knowledge JAR)包。这个包本质上是一个特殊的Maven构件,里面包含了所有的规则资产、数据模型以及一个描述规则依赖关系的kmodule.xml文件。
  • 发布到Maven仓库:WorkBench可以配置一个或多个远程Maven仓库(如Nexus、Artifactory)。点击“构建并部署”后,它会将KJAR打包,并发布到指定的仓库中。这个动作,就是“动态”的开关——你的应用程序将从仓库中拉取这个最新版本的KJAR。

2.4 运行时管理集成层

这一层是WorkBench与你的业务应用连接的桥梁。虽然WorkBench本身不直接在你的应用进程中执行规则,但它通过Maven仓库和KIE Server与应用紧密关联。

  • KIE Server:这是Drools/WildFly提供的独立规则执行服务器。你可以将KJAR部署到KIE Server上,然后通过REST或JMS接口远程调用规则。WorkBench可以管理多个KIE Server,并直接向它们部署规则包。这是一种解耦更彻底的SOA架构。
  • 嵌入式模式(更常见):更多时候,我们的Spring Boot或普通Java应用会以“嵌入式”的方式使用Drools。应用会作为一个Maven客户端,定期(或通过监听)从配置的Maven仓库中检查是否有新版本的KJAR,然后动态加载到本地的KieContainer中。这个“拉取-加载”的机制,是实现应用不重启而规则生效的核心。

理解了这四层,你就明白WorkBench不是一个孤立的工具,而是一个规则生命周期的管理中枢。它连接了规则的创作者(业务)、存储库(Git)、制品库(Maven)和执行器(你的应用或KIE Server)。

3. 实战:搭建Spring Boot应用与WorkBench的动态规则链路

理论讲完了,我们动手搭一套最小可用的环境。假设我们有一个简单的风控服务,需要根据用户等级和交易金额动态判断是否触发审核。

3.1 环境准备与WorkBench部署

首先,你需要一个WorkBench。最简单的方法是使用Docker运行官方镜像。

docker run -p 8080:8080 -p 8001:8001 -e KIE_ADMIN_USER=admin -e KIE_ADMIN_PWD=admin --name drools-wb jboss/business-central-workbench-showcase:latest

访问http://localhost:8080/business-central,用 admin/admin 登录。这里有几个关键配置点:

  • 配置Maven仓库:进入Menu -> Deploy -> Execution Servers,这里可以配置KIE Server。但对我们嵌入式场景,更重要的是配置Artifact Repository。在Menu -> Projects -> My Space -> Settings下,确保你的项目使用的仓库配置正确。通常你需要一个远程仓库(如公司内部的Nexus),在pom.xml中配置其地址和认证信息。为了演示,我们可以暂时使用WorkBench内置的仓库。
  • 创建空间和项目:点击“Design”视图,创建一个新的空间(Space),比如叫“RiskControl”。在空间内,点击“Add Project”,创建一个名为“risk-rules-kjar”的Maven项目。Group ID填com.example,Artifact ID就是risk-rules-kjar,版本号1.0.0

3.2 在WorkBench中创建数据模型和规则

我们的应用需要一个事实对象。由于WorkBench内定义复杂Java类不太方便,通常的做法是在应用侧定义好模型,打包成JAR,然后上传到WorkBench的项目依赖中。

1. 应用侧定义模型JAR:创建一个独立的Maven模块risk-model,定义风控事实对象。

// RiskTransaction.java package com.example.risk.model; import java.math.BigDecimal; public class RiskTransaction { private String userId; private String userLevel; // “VIP”, “NORMAL”, “NEW” private BigDecimal amount; private boolean needReview; // 规则执行结果 private String rejectReason; // 省略构造方法、getter、setter }

将这个模块打包mvn clean install,生成risk-model-1.0.0.jar

2. 在WorkBench中上传模型依赖:在“risk-rules-kjar”项目中,点击“Settings” -> “Dependencies” -> “Add from Repository”。如果你已将模型JAR部署到连接的Maven仓库,这里可以搜索添加。对于本地测试,可以点击“Upload”,选择刚生成的JAR文件上传。添加后,在项目根目录的pom.xml中会自动增加依赖。

3. 创建规则文件:在项目中,点击“Add Asset” -> “DRL file”,命名为TransactionReviewRule.drl

package com.example.risk.rules; import com.example.risk.model.RiskTransaction; // 规则1:新用户大额交易需审核 rule “New User Large Amount Review” when $t: RiskTransaction(userLevel == “NEW”, amount > 5000) then $t.setNeedReview(true); $t.setRejectReason(“新用户交易金额超过5000,需人工审核”); update($t); // 通知引擎事实已变更 end // 规则2:普通用户单笔超高额交易需审核 rule “Normal User Ultra Large Amount Review” when $t: RiskTransaction(userLevel == “NORMAL”, amount > 50000) then $t.setNeedReview(true); $t.setRejectReason(“普通用户单笔交易超过50000,需人工审核”); update($t); end // 规则3:VIP用户默认通过(示例,实际可能更复杂) rule “VIP User Auto Pass” when $t: RiskTransaction(userLevel == “VIP”) then $t.setNeedReview(false); $t.setRejectReason(null); update($t); end

4. 创建测试场景验证:点击“Add Asset” -> “Test Scenario”。创建一个测试,添加一个RiskTransaction事实,设置userLevel=“NEW”,amount=6000,然后断言执行后needReview==true。运行测试,确保规则逻辑正确。

5. 构建与部署KJAR:点击项目右上角的“Build” -> “Build & Deploy”。WorkBench会执行Maven构建,并将生成的KJAR(如risk-rules-kjar-1.0.0.jar)部署到其关联的Maven仓库中。记下这个产物的GAV坐标:com.example:risk-rules-kjar:1.0.0

3.3 Spring Boot应用动态加载规则

现在,我们来创建规则的使用方——一个Spring Boot应用。

1. 项目依赖:

<dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>7.73.0.Final</version> <!-- 请使用与WorkBench一致的版本 --> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <version>7.73.0.Final</version> </dependency> <!-- 你的模型模块 --> <dependency> <groupId>com.example</groupId> <artifactId>risk-model</artifactId> <version>1.0.0</version> </dependency>

2. 核心配置类:这里的关键是配置一个能动态感知远程仓库KJAR变化的KieContainer

@Configuration public class DroolsDynamicConfig { @Value(“${rules.releaseId:com.example:risk-rules-kjar:1.0.0}”) private String releaseIdStr; @Bean public KieContainer kieContainer() { KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( releaseIdStr.split(“:”)[0], // groupId releaseIdStr.split(“:”)[1], // artifactId releaseIdStr.split(“:”)[2] // version ); // 创建KieContainer,它会从配置的Maven仓库中拉取指定的KJAR KieContainer kContainer = kieServices.newKieContainer(releaseId); // !!!关键步骤:创建并启动一个Scanner,定期检查仓库中是否有新版本 KieScanner kScanner = kieServices.newKieScanner(kContainer); // 每10秒扫描一次(生产环境建议设置更长,如10分钟) kScanner.start(10000L); return kContainer; } @Bean public KieBase kieBase(KieContainer kieContainer) { return kieContainer.getKieBase(); } @Bean public KieSession kieSession(KieContainer kieContainer) { // 注意:KieSession通常是有状态的,且非线程安全。 // 生产环境一般通过ThreadLocal或池化管理,每次请求创建新Session。 return kieContainer.newKieSession(); } }

3. 在业务服务中使用规则:

@Service public class RiskService { @Autowired private KieContainer kieContainer; // 注入的是动态Container public RiskTransaction evaluateTransaction(RiskTransaction transaction) { // 每次执行都从Container中获取一个新的、无状态的StatelessKieSession // 或者获取有状态的,但必须确保每次使用后dispose StatelessKieSession kieSession = kieContainer.newStatelessKieSession(); kieSession.execute(transaction); // 执行规则 return transaction; } }

4. 应用配置文件:

# application.yml rules: releaseId: com.example:risk-rules-kjar:1.0.0 # 初始版本,与WorkBench部署的一致 # 配置Maven仓库地址,让Kie-ci知道从哪里拉取KJAR # 通常需要放在一个settings.xml文件中,并通过系统属性指定 # 这里简单演示,可以在启动参数中设置:-Dorg.kie.maven.settings.custom=/path/to/settings.xml

5. 动态更新验证:

  1. 启动你的Spring Boot应用。
  2. 应用会根据releaseId从Maven仓库拉取初始的1.0.0版本规则。
  3. 现在,业务人员觉得新用户的阈值5000太低了,想调到10000。他登录WorkBench,找到TransactionReviewRule.drl文件,将第一条规则的条件改为amount > 10000
  4. 修改后,点击“Build & Deploy”。WorkBench会构建并发布一个新版本,比如1.0.1
  5. 你的Spring Boot应用中配置的KieScanner会在下一个扫描周期(我们设的10秒)检测到仓库中com.example:risk-rules-kjar有了新版本1.0.1
  6. KieScanner会自动下载新版本的KJAR,并更新内部的KieContainer这个过程不需要重启应用
  7. 此后,所有通过kieContainer.newStatelessKieSession()获取的会话,都将使用最新的1.0.1版本的规则逻辑。

4. 深入动态规则更新的内部机制与生产级考量

上面的流程跑通了,但真要上生产,有几个深坑你必须提前知道。动态更新的便利性背后,是复杂的状态管理和一致性挑战。

4.1 KieContainer更新到底发生了什么?

KieScanner检测到新版本并触发KieContainer.updateToVersion(newReleaseId)时,底层会创建一个全新的KieBaseKieSession定义。但是,已经存在的、正在使用的KieSession对象不会自动更新。它们仍然引用着旧的KieBase。这就是为什么在我们的示例中,每次执行都通过kieContainer.newStatelessKieSession()重新获取会话。对于有状态的会话(StatefulKieSession),问题更复杂:

  • 内存中的事实(Facts):旧会话中已经插入的事实不会迁移到新会话中。如果你的规则引擎需要维护一个长时间运行的会话(例如,监控一个用户的所有交易序列),直接更新Container会导致会话状态丢失。
  • 解决方案:对于有状态会话,通常采用“双缓冲”或“会话迁移”策略。例如,维护一个当前活跃会话的引用。当更新发生时,暂停新事件的注入,将旧会话中的所有事实通过getFactHandles()getObject()提取出来,插入到一个基于新Container创建的新会话中,然后再切换流量。这个过程需要业务层做协调,保证数据一致性。

4.2 版本管理与灰度发布

直接让所有应用实例瞬间切换到新规则是危险的。WorkBench和KIE Server提供了对多目标环境的部署能力,但嵌入式模式下需要自己实现灰度。

  • 策略一:应用侧版本控制。不要在配置中写死版本号(如1.0.0),而是将其外部化,比如存放到配置中心(Apollo, Nacos)。更新规则时,先在WorkBench发布新版本(如1.0.1),然后在配置中心将一小部分机器的规则版本号改为1.0.1。这些机器的KieContainer会自动更新,实现灰度。
  • 策略二:规则路由。在应用层做一个路由,根据用户ID、设备ID等将请求分派到不同版本的KieContainer上。可以同时加载多个版本的KJAR,创建多个KieContainer实例。

4.3 性能、缓存与类加载器隔离

  • 构建开销:每次从仓库拉取KJAR并构建KieBase是有成本的,虽然Drools会缓存编译结果。频繁扫描(如10秒一次)在高并发下可能带来压力。生产环境建议将扫描间隔设置为分钟级(如300000毫秒),并通过Webhook等机制被动触发更新(WorkBench部署后调用应用的刷新接口)。
  • 永久代/元空间溢出:每次加载新版本的KJAR,都会创建一个新的类加载器来加载其中的类。如果频繁更新且不重启应用,旧的类加载器可能因为仍有引用而无法被GC,导致元空间内存持续增长。这是Java类加载器隔离机制带来的固有风险。一个缓解办法是,不要过于频繁地发布小版本,或者定期重启应用实例。
  • Session池化:对于无状态会话(StatelessKieSession),由于其线程安全且无状态,可以池化以提升性能。Apache Commons Pool是一个不错的选择。当Container更新后,需要清空并重新初始化整个会话池。

4.4 监控与回滚

  • 监控规则执行:你需要知道新规则上线后的效果。可以定义规则监听器(RuleRuntimeEventListener,AgendaEventListener),将规则的触发、匹配、执行情况记录到日志或Metrics系统(如Prometheus),便于业务验证。
  • 快速回滚:当新规则发现问题时,回滚机制必须迅速。如果使用配置中心,直接回滚版本号配置即可。也可以利用Git的版本控制,在WorkBench中快速将项目回退到上一个稳定版本Tag,然后重新构建部署。

5. 避坑指南:那些我踩过的“动态”之坑

纸上得来终觉浅,绝知此事要踩坑。下面分享几个我在实际项目中遇到的典型问题。

5.1 事实对象模型变更的兼容性问题

这是最棘手的问题之一。假设业务发展,需要在RiskTransaction里加一个字段String location。你在应用侧更新了risk-model模块,并发布了新JAR。然后你在WorkBench中更新项目依赖,并修改规则,引用了这个新字段。

坑点:如果你的Spring Boot应用先更新了模型JAR(应用重启部署),而WorkBench中的规则还未更新到新版本,那么当旧的规则KJAR(仍引用旧的模型类)被加载时,会抛出ClassNotFoundExceptionNoSuchMethodError,因为运行时找不到规则中引用的新字段或方法。

解决方案:必须严格管理模型与规则的版本发布顺序。

  1. 向后兼容性优先:模型变更尽量只新增字段,不删除或修改已有字段。如果必须做破坏性变更,则视为一个全新的规则项目(新的artifactId或major version)。
  2. 同步发布流程:制定标准的发布流程:先发布新模型JAR到Maven仓库 -> 在WorkBench中更新项目依赖并验证 -> 修改和测试规则 -> 发布新规则KJAR。在应用侧,模型的更新和规则的更新最好在同一个应用发布窗口内完成,或者确保应用能同时兼容新旧模型一段时间(通过模型版本化)。

5.2 WorkBench中Git仓库的维护与备份

WorkBench内置的Git仓库如果损坏,所有规则资产将丢失。虽然你可以通过git clone命令拉取备份(WorkBench的Git仓库地址通常在http://localhost:8080/business-central/git/your-space),但这应该是最后的手段。

最佳实践

  • 定期备份:编写脚本,定期通过Git命令克隆所有空间的项目到安全位置。
  • 使用外部Git仓库:更高级的用法是将WorkBench项目配置为使用外部Git仓库(如GitLab)作为远程源。这样所有更改都会推送到外部仓库,实现了天然的备份和更强大的协作功能。这需要在WorkBench的系统配置中进行设置。
  • 项目导出:对于关键项目,定期使用WorkBench的“Export Project”功能导出为ZIP包。

5.3 规则调试与日志的困境

在动态环境下,规则出了问题,日志散落在哪里?WorkBench的测试场景只能在开发时用。生产环境规则执行出错,你看到的可能只是一条模糊的异常信息。

我的经验

  • 在规则中主动日志:在DRL的RHS(then部分)中,使用System.out.println是下策,因为会打到应用服务器的标准输出,很难关联。更好的做法是注入一个日志服务。
    import com.example.service.RuleLogger; rule “Some Rule” when $t: RiskTransaction(...) $logger: RuleLogger() // 通过全局变量注入 then $logger.log(“Rule triggered for user: ” + $t.getUserId()); // ... 业务操作 end
    在创建KieSession时,通过kieSession.setGlobal(“logger”, ruleLoggerInstance)注入一个全局的日志Bean,这个Bean可以将日志结构化地输出到你的ELK或SLS。
  • 启用Drools审计日志:可以通过事件监听器获取详细的规则执行轨迹,但注意性能开销,建议只在调试时开启。
  • 给规则打“标签”:在规则属性中增加@tag(“风控-新用户”),这样在日志中可以通过标签快速过滤出是哪些规则被触发了。

5.4 性能热点:复杂的规则网络与Rete算法

当规则数量达到数百上千条,且事实对象复杂时,规则引擎的匹配阶段(Rete算法)可能成为性能瓶颈。特别是在动态更新后,新的规则网络需要重新构建。

优化方向

  • 规则设计:尽量避免“交叉乘积”式的条件组合。使用规则属性如salience(优先级)、agenda-group(议程组)、activation-group(激活组)来合理控制规则执行顺序和互斥性,减少无谓的匹配。
  • 事实设计:确保作为条件的事实对象实现了正确的hashCode()equals()方法,这能极大提升Rete节点的匹配效率。
  • 会话管理:对于无状态会话,务必使用会话池。对于有状态会话,定期评估是否需要将部分长时间存在的事实“老化”或移出工作内存。
  • 监控匹配时间:通过AgendaEventListener监控规则匹配和触发的时间,找出那些执行缓慢的“热点”规则进行优化。

动态规则不是银弹,它用管理的复杂性换来了业务的灵活性。在决定引入这套架构前,一定要评估你的业务规则变更是否真的如此频繁,以及你的团队是否准备好应对随之而来的运维和治理挑战。对于规则相对稳定,或者变更可以通过功能开关、配置中心简单实现的场景,直接用静态规则或轻量级的脚本引擎(如Aviator、QLExpress)可能是更简单高效的选择。但一旦你面临的是成百上千条、由非技术人员频繁维护的复杂业务规则,Drools WorkBench这套动态规则管理体系,依然是经过大量实战检验的、可靠的企业级解决方案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/15 5:30:09

数据校验技术全解析:从奇偶校验到CRC的实战选型指南

1. 从一次通信故障说起&#xff1a;为什么校验如此重要去年&#xff0c;我负责的一个工业数据采集项目遇到了一个诡异的问题。现场PLC通过串口向服务器发送一批16位的传感器数据&#xff0c;大部分时候都正常&#xff0c;但偶尔会收到一个明显超出量程的数值&#xff0c;比如温…

作者头像 李华
网站建设 2026/8/15 5:28:52

Git忽略文件(.gitignore)完全指南:从语法到实战配置

1. 项目概述&#xff1a;为什么你的代码仓库总是“脏”的&#xff1f; 每次提交代码前&#xff0c;你是不是总要花几分钟&#xff0c;手动把 node_modules 、 .DS_Store 、 *.log 这些文件从暂存区里剔除出去&#xff1f;或者更糟&#xff0c;一个不小心&#xff0c;就把…

作者头像 李华
网站建设 2026/8/15 5:27:57

IntelliJ IDEA配置Maven全攻略:从环境搭建到项目运行

1. 项目概述&#xff1a;为什么新手需要这份配置指南&#xff1f;如果你刚接触Java开发&#xff0c;或者从Eclipse等IDE转过来&#xff0c;第一次在IntelliJ IDEA里运行一个Maven项目&#xff0c;大概率会卡在第一步。我见过太多新手&#xff0c;兴冲冲地从GitHub上clone了一个…

作者头像 李华
网站建设 2026/8/15 5:27:33

Windows批处理脚本实战:基于文件名关键词的批量文件自动分类与归档

1. 项目概述&#xff1a;从手动拖拽到一键归集你有没有经历过这样的场景&#xff1a;电脑桌面上散落着几十个、上百个文件&#xff0c;它们可能是不同项目的工作文档、从网上下载的零散资料&#xff0c;或者是一堆需要归档的照片。它们的文件名里可能包含了日期、项目编号或类型…

作者头像 李华
网站建设 2026/8/15 5:27:15

CocoaPods安装全攻略:从网络优化到环境配置的终极解决方案

1. 项目概述&#xff1a;CocoaPods安装的“世纪难题”搞iOS开发&#xff0c;尤其是刚接触的新手&#xff0c;或者换了一台新Mac&#xff0c;十有八九会在安装CocoaPods这一步上栽跟头。那个经典的sudo gem install cocoapods命令敲下去&#xff0c;屏幕上的光标就开始闪烁&…

作者头像 李华
网站建设 2026/8/15 5:26:40

队列数据结构实战:从约瑟夫环问题理解FIFO原理与应用

1. 项目概述&#xff1a;从“围圈报数”到队列的实战演练最近在带学生刷《信息学奥赛一本通》的题目&#xff0c;做到第1334题“【例2-3】围圈报数”时&#xff0c;发现很多初学者对“队列”这个数据结构的概念和应用场景理解得不够透彻。这道题本身是一个经典的约瑟夫环问题的…

作者头像 李华