遍历有向网格链路实现

news/2024/9/17 7:54:33/ 标签: 算法, 开发语言, java

在实际的业务中,我们可能遇到复杂规则(多个或与条件组合),复杂链路等类似场景问题,如:规则引擎相关业务,生产任务排期等。

复杂链路示意图如下:
在这里插入图片描述

复杂网路链路场景描述

  1. 有一个或多个开始节点,有一个或多个结束节点;
  2. 各节点通过有向箭头来描述节点之间的关系;
  3. 关系节点之间不可形成回路;
  4. 节点数量不固定,关系不固定。

程序如何算出所有链路?

设计思路:

节点场景:

  • 开始节点:如图编号1所示
  • 中间节点:如图编号2所示
  • 终止节点:如图编号3所示
  • 零节点:没有关系的节点,如图编号4所示

在这里插入图片描述

如何定义数据模型去描述节点之间的关系呢?

java">@Data
public class LinkItem {// 该节点IDprivate Integer id;// 可到达该节点的ID列表private List<Integer> pre;// 该节点可以到达哪些节点的ID列表private List<Integer> next;public LinkItem(Integer id, List<Integer> pre, List<Integer> next) {this.id = id;this.pre = pre;this.next = next;}
}

如何校验回路链路?

如下图形成了回路:
在这里插入图片描述
思路:链路是由一个个节点有序链接而成,出现了回路,就说明遍历到该节点时,该节点或该节点的next节点出现在该链路中了。

关键代码

java">package com.example.demo.util;import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.example.demo.domain.Link;
import com.example.demo.domain.LinkItem;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;@Slf4j
public class LinkHandlerUtil {public static List<Link> getAllLink(List<LinkItem> linkItems) {if (CollUtil.isEmpty(linkItems)) {log.info("LinkHandlerUtil.getRuleCondition(), linkItems is null");throw new RuntimeException("参数为空");}// 链路数据处理List<Link> result = new ArrayList<>();handlerLinkData(result, linkItems);return result;}/*** 找出所有链路数据** @param list* @param linkItems*/private static void handlerLinkData(List<Link> list, List<LinkItem> linkItems) {// 1.找出初始链路for (LinkItem linkItem : linkItems) {if (CollUtil.isEmpty(linkItem.getPre())) {linkItemHandler(linkItem, list, linkItems);}}// 2. 递归出所有链路boolean flag = allLinkIsEnd(list, linkItems);while (!flag) {for (LinkItem linkItem : linkItems) {if (CollUtil.isNotEmpty(linkItem.getPre())) {linkItemHandler(linkItem, list, linkItems);}}flag = allLinkIsEnd(list, linkItems);}}/*** 校验该链路是否结束** @param link* @param linkItems* @return*/private static boolean linkIsEnd(Link link, List<LinkItem> linkItems) {List<Integer> itemIds = link.getItemIds();LinkItem linkItem = getItemById(itemIds.get(itemIds.size() - 1), linkItems);if (CollUtil.isNotEmpty(linkItem.getNext())) {return false;}return true;}/*** 判断所有链路是否都结束了** @param links* @param linkItems* @return*/private static boolean allLinkIsEnd(List<Link> links, List<LinkItem> linkItems) {if(CollUtil.isEmpty(links)) {return true;}for (Link link : links) {boolean flag = linkIsEnd(link, linkItems);if (!flag) {return false;}}return true;}/*** 获取ItemById* @param id* @param linkItems* @return*/private static LinkItem getItemById(Integer id, List<LinkItem> linkItems) {for (LinkItem linkItem : linkItems) {if (linkItem.getId().equals(id)) {return linkItem;}}return null;}/*** 节点校验* @param id* @param link* @param linkItems*/private static void itemIdIsValid(Integer id, Link link, List<LinkItem> linkItems) {LinkItem linkItem = getItemById(id, linkItems);if (null == linkItem) {throw new RuntimeException("参数linkItem为空");}// 链路是否包含当前节点校验List<Integer> itemIds = link.getItemIds();if (itemIds.contains(linkItem.getId())) {throw new RuntimeException("参数链路规则校验失败");}// 链路是否包含当前节点的next节点校验if (CollUtil.isNotEmpty(linkItem.getNext())) {for (Integer itemId : linkItem.getNext()) {if (itemIds.contains(itemId)) {throw new RuntimeException("参数链路规则校验失败");}}}}/*** 节点链路处理* @param linkItem* @param list* @param linkItems*/private static void linkItemHandler(LinkItem linkItem, List<Link> list, List<LinkItem> linkItems) {// 场景1: pre-无,next-无if (CollUtil.isEmpty(linkItem.getPre()) && CollUtil.isEmpty(linkItem.getNext())) {Link link = new Link();List<Integer> itemIds = new ArrayList<>();itemIds.add(linkItem.getId());link.setItemIds(itemIds);list.add(link);return;}// 场景2:pre-无, next-有,开始节点,链路中需要添加当前节点和next节点if (CollUtil.isEmpty(linkItem.getPre()) && CollUtil.isNotEmpty(linkItem.getNext())) {for (Integer id : linkItem.getNext()) {Link link = new Link();List<Integer> itemIds = new ArrayList<>();itemIds.add(linkItem.getId());itemIds.add(id);link.setItemIds(itemIds);list.add(link);}return;}// 场景3:pre-有, next-无,终止节点,链路无需处理if (CollUtil.isNotEmpty(linkItem.getPre()) && CollUtil.isEmpty(linkItem.getNext())) {// 由于终止节点已经在场景4里面添加了,所以此处无需任何处理return;}// 场景4: pre-有, next-有,中间节点,由于当前节点已经在场景2添加过了,此时只需要添加next节点if (CollUtil.isNotEmpty(linkItem.getPre()) && CollUtil.isNotEmpty(linkItem.getNext())) {if(CollUtil.isEmpty(list)) {throw new RuntimeException("参数校验失败,中间节点链路不可为空");}List<Link> newList = new ArrayList<>();List<Link> removeList = new ArrayList<>();// 先找出该节点对应的链路for (Link link : list) {List<Integer> itemIds = link.getItemIds();Integer id = link.getItemIds().get(itemIds.size() - 1);if (id.equals(linkItem.getId())) {for (int i = 0; i < linkItem.getNext().size(); i++) {// 校验当前节点是否合法itemIdIsValid(linkItem.getNext().get(i), link, linkItems);// 删除原来的链路removeList.add(link);// 补充一个新的链路,并将该节点的next节点加入链路中Link newLink = new Link();List<Integer> newRuleItems = new ArrayList<>();newRuleItems.addAll(link.getItemIds());newRuleItems.add(linkItem.getNext().get(i));newLink.setItemIds(newRuleItems);newList.add(newLink);}}}if (CollUtil.isNotEmpty(newList)) {list.removeAll(removeList);list.addAll(newList);}}}}

Link:

java">@Data
public class Link {private List<Integer> itemIds;
}

结果验证

该数据来源于本文开头的示意图。

java">public static void main(String[] args) {List<LinkItem> linkItems = new ArrayList<>();linkItems.add(new LinkItem(1, null, Arrays.asList(2, 6)));linkItems.add(new LinkItem(2, Arrays.asList(1, 6), Arrays.asList(3, 7)));linkItems.add(new LinkItem(3, Arrays.asList(2, 10), Arrays.asList(4, 12)));linkItems.add(new LinkItem(4, Arrays.asList(3, 11), Arrays.asList(5, 7)));linkItems.add(new LinkItem(5, Arrays.asList(4, 8, 12), null));linkItems.add(new LinkItem(6, Arrays.asList(1), Arrays.asList(2)));linkItems.add(new LinkItem(7, Arrays.asList(2, 4), Arrays.asList(8)));linkItems.add(new LinkItem(8, Arrays.asList(7), Arrays.asList(5)));linkItems.add(new LinkItem(9, null, Arrays.asList(10, 13)));linkItems.add(new LinkItem(10, Arrays.asList(9), Arrays.asList(3)));linkItems.add(new LinkItem(11, Arrays.asList(12), Arrays.asList(4)));linkItems.add(new LinkItem(12, Arrays.asList(3), Arrays.asList(5, 11)));linkItems.add(new LinkItem(13, Arrays.asList(9), Arrays.asList(15, 17)));linkItems.add(new LinkItem(15, Arrays.asList(13), Arrays.asList(16)));linkItems.add(new LinkItem(16, Arrays.asList(15), Arrays.asList(17)));linkItems.add(new LinkItem(17, Arrays.asList(13, 16), null));linkItems.add(new LinkItem(18, null, null));linkItems.add(new LinkItem(19, null, Arrays.asList(20)));linkItems.add(new LinkItem(20, Arrays.asList(19), null));List<Link> links = getAllLink(linkItems);for (Link link : links) {log.info("{}", JSON.toJSONString(link));}
}

运行结果:

截图红框就是本文示意图的所有链路。
在这里插入图片描述


http://www.ppmy.cn/news/1521870.html

相关文章

单片机学习笔记

一、单片机帝国的诞生与发展 1.1 单片机的基本概念 单片机是一种集成电路芯片&#xff0c;采用超大规模的集成电路把具有数据处理功能的中央处理器存储器、输入输出端口、外围电路和相关外设集成在一块硅片上构成一个小而完整的微型计算机系统。 一般而言&#xff0c;单片机也…

光电振荡器行业研究:未来几年年复合增长率CAGR为16.0%

光电振荡器(OEO)是一种微波光子系统&#xff0c;它使用高品质因数的光能量存储元件产生具有超低相位噪声的微波信号。光电振荡器基于将来自泵浦激光器的连续光能转换为射频(RF)、微波或毫米波信号。OEO 的特点是具有非常高的品质因数(Q) 和稳定性&#xff0c;以及电子振荡器不容…

K8s中如何使用etcd进行集群信息的备份与恢复

这里写目录标题 ETCD是什么?1. **`etcd`(服务)**2. **`etcdctl`(客户端工具)**如何安装etcdctl(客户端工具)查看目前K8s自带etcd中的版本信息安装对应版本的etcdutl工具下载 `etcdutl` 3.5.7 版本配置环境变量创建备份文件验证一下备份的快照文件备份文件恢复的效果演示…

从零开始学数据结构系列之第五章《B树的删除2》

文章目录 样例3情况3案例1案例2 总结往期回顾 样例3 原图&#xff0c;本例要删除50这个关键字&#xff0c;那这要怎么做呢&#xff1f; 思考ing 思考ing 思考ing 思考ing 思考ing 可以看到&#xff0c;最左边的子树是没有变化的&#xff0c;所以直接就不变&#xff0c;最…

语音测试(一)ffmpeg视频转音频

视频转音频 下载ffmpeg工具进入bin目录cmd进入控制台输入命令 ffmpeg.exe -i ./视频.mp4 ./音频.wav命令说明 ffmpeg -i input.mp4 output.mkv FFmpeg 可能会尝试自动选择合适的编码器对视频和音频进行重新编码&#xff0c;以便适应 MKV 格式的要求ffmpeg -i input.mp4 -c c…

Node.js 高级主题深度解析:性能优化、测试与日志管理

Node.js 高级主题深度解析&#xff1a;性能优化、测试与日志管理 目录 &#x1f680; 性能优化 &#x1f6e0;️ 使用 cluster 模块实现多进程&#x1f9e0; 内存泄漏分析与优化&#x1f4ca; 性能分析工具的使用 &#x1f9ea; 测试 &#x1f4d1; 单元测试和集成测试&#x…

使用Unity的准备

下载Unity 下载Unity Hub Unity - 实时内容开发平台 | 3D、2D、VR & AR可视化https://unity.cn/ 创建账号或者登入账号 Unity安装 路径尽量为英文路径 登入账号 点击头像登入账号 这里已经登入 打开偏好 设置中文 添加许可证 获取免费版的即可 安装编辑器 新建项目…

【HTML】置换元素(替换元素)

● 它的内容不是由元素的标签内的内容决定的&#xff0c;而是由元素的属性决定的 ● 可以通过CSS设置宽度和高度。 常见的置换元素主要包括以下几种&#xff1a; <img> 元素&#xff1a;用于嵌入图像&#xff0c;通过 src 属性指定图像的路径。例如&#xff1a;<img…

pdf在线转换成word免费版,一键免费转换

在日常的学习和办公中&#xff0c;PDF文件和Word文档是我们离不开的两种最常见的文件&#xff0c;而PDF与Word文档之间的转换成为了我们日常工作中不可或缺的一部分。无论是为了编辑、修改还是共享文件&#xff0c;掌握多种PDF转Word的方法都显得尤为重要。很多小伙伴关心能不能…

2023年公共英语三级考试阅读经典试题及译文答案

2023年公共英语三级考试阅读经典试题及译文答案 Flying over a desert area in an airplane, two scientists looked down with trained eyes at treesand bushes. After an hour s flight, one of the scientists wrote in his book, "Look here for probable metal. &qu…

智能对决:提示词攻防中的AI安全博弈

智能对决&#xff1a;提示词攻防中的AI安全博弈 在2024年上海AIGC开发者大会上&#xff0c;知名提示词爱好者工程师云中嘉树发表了关于AI提示词攻防与安全博弈的精彩演讲。他深入探讨了当前AI产品的安全现状&#xff0c;提示词攻击的常见手段及其应对策略。本文将对他的演讲进…

Charles抓包全流程(Mac端+iOS端)

文章目录 与其他抓包软件的对比FiddlerWireShark Charles下载安装及配置Charles抓包实践小结 Charles Proxy是一个广泛使用的网络调试代理工具&#xff0c;它允许开发者监控和分析所有经过计算机的HTTP和SSL/HTTPS网络流量信息。 与其他抓包软件的对比 Fiddler Charles 支持多…

【PPT学习笔记】使用PPT制作动画/手书/视频等作品的适配性和可能性?

【PPT学习笔记】使用PPT制作动画/手书等作品的可能性&#xff1f; 背景前摇&#xff1a;&#xff08;省流可不看&#xff09; 最近找到另外一份新的实习工作&#xff0c;有很多需要用到PPT动画的地方。 然而&#xff0c;我们之前制作的理工科PPT全是摒弃了形式主义的艰苦朴素…

2024.9.6 作业

手写unique_ptr指针指针 代码&#xff1a; #include <iostream> #include <stdexcept>template <typename T> class unique_ptr { public:// 构造函数explicit unique_ptr(T* ptr nullptr) : m_ptr(ptr) {}// 析构函数~unique_ptr() {delete m_ptr;}// 禁…

ASP.Net Core 因集成WebSocket导致Swagger UI显示错误

文章目录 前言一、ApiExplorerSettings二、解决Swagger UI显示问题 前言 Swagger UI 本身并不支持直接展示或测试 WebSocket 端点。Swagger&#xff08;现在称为 OpenAPI&#xff09;及其 UI 实现主要是为 RESTful API 设计的&#xff0c;这些 API 基于 HTTP 请求/响应模型。W…

vue2+ueditor集成秀米编辑器

一、百度富文本编辑器 1.首先下载 百度富文本编辑器 下载地址&#xff1a;GitHub - fex-team/ueditor: rich text 富文本编辑器 2.把下载好的文件整理好 放在图片目录下 3. 安装插件vue-ueditor-wrap npm install vue-ueditor-wrap 4.在你所需要展示的页面 引入vue-uedito…

uniapp本地上传照片并转化为base64格式

const upPhoto async () > { try { // 选择图片 const result await uni.chooseImage({ count: 1, sizeType: [original, compressed], sourceType: [album, camera] }); if (resul…

Wimdows使用Appium IOS自动化

启动appium服务器&#xff1a; appium -a 127.0.0.1 -p 4724 配置 { "platformName": "iOS", "appium:platformVersion": "16.5.1", "appium:deviceName": "(★StatTrak™) |午夜黑&#xff08;崭新出厂&#…

springboot+vue+mybatis计算机毕业设计学科竞赛系统+PPT+论文+讲解+售后

学科竞赛系统的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品&#xff0c;体验高科技时代带给人们的方便&#xff0c;同时也能让用户体会到与以往常规产品不同的体验风格。 与安卓&#xff0c;iOS相比较起来&#xff0c;学科…

重修设计模式-创建型-工厂模式

重修设计模式-创建型-工厂模式 一、概述 工厂模式&#xff08;Factory Pattern&#xff09;是设计模式中非常基础且常用的一种模式&#xff0c;主要目的是通过封装对象的创建过程&#xff0c;从而实现代码的解耦和灵活性的提升。 工厂模式的核心思想 封装对象的创建&#x…