遍历有向图链路(DFS算法)- 优化版

embedded/2024/10/16 2:25:52/

在上一节基础上,去除了节点的pre集合,只保留节点next的结合,对数据模型进行了优化,实现思想做了优化。

有向图示例:

在这里插入图片描述

基本思路

  1. 构建有向图数据模型
  2. 校验有向图不能出现回路,即当前节点不能出现在历史链路中
  3. 首先找出有向图的初始节点
  4. 找出有向图的初始链路
  5. 链路是从开始节点,按照顺序累加而形成的
  6. 根据节点的next集合,递归遍历初始链路,进而获取所有链路

数据模型:

  1. 节点数据模型:
java">package com.angel.ocean.domain.dsf;import lombok.Data;
import java.util.List;@Data
public class ChainItem {// 该节点IDprivate Integer id;// 该节点可以到达哪些节点的ID列表private List<Integer> next;public ChainItem(Integer id, List<Integer> next) {this.id = id;this.next = next;}
}
  1. 有向图链路数据模型:
java">package com.angel.ocean.domain.dsf;import java.util.List;public class Chain {// ID链路private List<Integer> chainItemIds;// 是否结束private boolean end = false;public List<Integer> getChainItemIds() {return chainItemIds;}public void setChainItemIds(List<Integer> chainItemIds) {this.chainItemIds = chainItemIds;}public boolean isEnd() {return end;}public void setEnd(boolean end) {this.end = end;}
}

算法实现

java">package com.angel.ocean.utils;import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson2.JSON;
import com.angel.ocean.domain.dsf.Chain;
import com.angel.ocean.domain.dsf.ChainItem;
import lombok.extern.slf4j.Slf4j;
import java.util.*;@Slf4j
public class ChainHandlerUtil {/*** 获取所有链路* @param chainItems* @return*/public static List<Chain> getAllChain(List<ChainItem> chainItems) {if (CollUtil.isEmpty(chainItems)) {log.info("ChainHandlerUtil.getAllChain(), chainItems is null");throw new RuntimeException("参数为空");}// 链路数据List<Chain> list = new ArrayList<>();// 1. 获取初始节点List<ChainItem> firstItemList = getFirstItemList(chainItems);if(CollUtil.isEmpty(firstItemList)) {throw new RuntimeException("参数校验失败,不存在初始节点");}// 2. 获取初始链路for (ChainItem chainItem : firstItemList) {List<Integer> chainItemIds = new ArrayList<>();chainItemIds.add(chainItem.getId());Chain chain = new Chain();chain.setChainItemIds(chainItemIds);// 是否为终止链路,终止链路设置为trueif(CollUtil.isEmpty(chainItem.getNext())) {chain.setEnd(true);}list.add(chain);}// 3. 根据初始链路递归出所有链路数据// 是否所有链路都结束了boolean allChainIsEnd = false;while (!allChainIsEnd) {list = chainDataHandler(list, chainItems);allChainIsEnd = true;for (Chain chain : list) {if(!chain.isEnd()) {allChainIsEnd = false;}}}return list;}/*** 获取初始节点列表,不存在于next中的节点就是初始节点* @param chainItems* @return*/private static List<ChainItem> getFirstItemList(List<ChainItem> chainItems) {// 非初始节点集合Set<Integer> nextItemIds = new HashSet<>();for (ChainItem chainItem : chainItems) {if(CollUtil.isNotEmpty(chainItem.getNext())) {nextItemIds.addAll(chainItem.getNext());}}// 初始节点集合List<ChainItem> firstItemIds = new ArrayList<>();for (ChainItem chainItem : chainItems) {if(!nextItemIds.contains(chainItem.getId())) {firstItemIds.add(chainItem);}}return firstItemIds;}/*** 链路数据迭代* @param list* @param chainItems* @return*/private static List<Chain> chainDataHandler(List<Chain> list, List<ChainItem> chainItems) {List<Chain> newList = new ArrayList<>();for (Chain chain: list) {if(chain.isEnd()) {newList.add(chain);continue;}List<Integer> chainItemIds = chain.getChainItemIds();int chainEndItemId = chainItemIds.get(chainItemIds.size() - 1);ChainItem chainEndItem = getChainItemById(chainEndItemId, chainItems);for (Integer id : chainEndItem.getNext()) {// 是否为回路校验if(chainItemIds.contains(id)) {throw new RuntimeException("参数校验失败,链路出现回路");}Chain newChain = new Chain();List<Integer> newChainItemIds = new ArrayList<>();newChainItemIds.addAll(chainItemIds);newChainItemIds.add(id);newChain.setChainItemIds(newChainItemIds);ChainItem nextItem = getChainItemById(id, chainItems);// 是否为终止链路,终止链路设置为trueif(CollUtil.isEmpty(nextItem.getNext())) {newChain.setEnd(true);}newList.add(newChain);}}return newList;}/*** 获取ItemById** @param id* @param chainItems* @return*/private static ChainItem getChainItemById(Integer id, List<ChainItem> chainItems) {for (ChainItem chainItem : chainItems) {if (chainItem.getId().equals(id)) {return chainItem;}}return null;}
}

算法验证

java">public static void main(String[] args) {// 上述有向图可以转换成如下数据List<ChainItem> chainItems = new ArrayList<>();chainItems.add(new ChainItem(1, Arrays.asList(2, 6)));chainItems.add(new ChainItem(2, Arrays.asList(3, 7)));chainItems.add(new ChainItem(3, Arrays.asList(4, 12)));chainItems.add(new ChainItem(4, Arrays.asList(5, 7)));chainItems.add(new ChainItem(5,  null));chainItems.add(new ChainItem(6, Arrays.asList(2)));chainItems.add(new ChainItem(7, Arrays.asList(8)));chainItems.add(new ChainItem(8, Arrays.asList(5)));chainItems.add(new ChainItem(9, Arrays.asList(10, 13)));chainItems.add(new ChainItem(10, Arrays.asList(3)));chainItems.add(new ChainItem(11, Arrays.asList(4)));chainItems.add(new ChainItem(12, Arrays.asList(5, 11)));chainItems.add(new ChainItem(13, Arrays.asList(15, 17)));chainItems.add(new ChainItem(15, Arrays.asList(16)));chainItems.add(new ChainItem(16, Arrays.asList(17)));chainItems.add(new ChainItem(17, null));chainItems.add(new ChainItem(18, null));chainItems.add(new ChainItem(19, Arrays.asList(20)));chainItems.add(new ChainItem(20, null));List<Chain> chains = getAllChain(chainItems);for (Chain chain : chains) {log.info("{}", JSON.toJSONString(chain));}
}

运行结果:

在这里插入图片描述


http://www.ppmy.cn/embedded/127388.html

相关文章

Java面试宝典-Java集合02

目录 Java面试宝典-Java集合02 21、TreeMap 和 TreeSet 在排序时如何比较元素&#xff1f; 22、ArrayList 和 LinkedList 的区别是什么&#xff1f; 23、ArrayList 和 Vector 的区别&#xff1f; 24、队列和栈是什么&#xff1f;有什么区别&#xff1f; 25、Queue和Deque的区别…

基于 RISCV 的裸机程序

基于 RISCV 的裸机程序 基于 RISCV 的裸机程序配置环境标准 rust 代码通过 `cargo` 生成一段标准代码搭建裸机程序添加退出系统调用添加 `println!` 宏参考文档基于 RISCV 的裸机程序 配置环境 ansen@ansen-virtual-machine:~$ rustc --version rustc 1.83.0-nightly (9ff5fc…

Spring Boot 3新特性@RSocketExchange轻松实现消息实时推送

Spring Boot 3新特性RSocketExchange轻松实现消息实时推送 随着微服务架构的普及&#xff0c;实时消息推送成为许多现代应用程序的核心需求。Spring Boot 3引入了RSocketExchange注解&#xff0c;这一新特性使得开发者能够轻松实现消息实时推送&#xff0c;极大地简化了客户端…

conda新建环境中存在大量ros相关python包

1 问题现象 新建的conda环境&#xff0c;执行pip list&#xff0c;出现了大量的ros相关包&#xff0c;环境不纯净。重新安装anaconda没有用。 2 问题原因 2.1 执行python -m site 执行python -m site获得以下结果 其中sys.path包含了’/opt/ros/noetic/lib/python3/dist-…

【unity框架开发12】从零手搓unity存档存储数据持久化系统,实现对存档的创建,获取,保存,加载,删除,缓存,加密,支持多存档

文章目录 前言一、Unity对Json数据的操作方法一、JsonUtility方法二、Newtonsoft 二、持久化的数据路径三、数据加密/解密加密方法解密方法 四、条件编译指令限制仅在编辑器模式下进行加密/解密四、数据持久化管理器1、存档工具类2、一个存档数据3、存档系统数据类4、数据存档存…

Linux的GDB学习与入门

GDB GDB&#xff08;GNU Debugger&#xff09;是一个功能强大的调试工具&#xff0c;广泛用于调试 C、C 和其他编程语言编写的程序。它是 GNU 项目的一部分&#xff0c;专为帮助开发者在程序执行时检测和修复错误设计。GDB 能够控制程序的执行&#xff0c;查看程序内部的状态&…

管家婆-本地化-重装数据库导入数据库mdf——未来之窗数据恢复专家

一、进入数据库管理软件 二、数据附加 三、选择文件mdf 四、错误处理 关闭管家婆和数据库服务重启 五、确定文件 六、确认附加 七、替换管家婆账套 八、阿雪技术观 拥抱开源与共享&#xff0c;见证科技进步奇迹&#xff0c;畅享人类幸福时光&#xff01; 让我们积极投身于技术…

C#中,重载(overload) 重写(override)的应用说明

一.重载(overload)& 重写(override)定义说明 1.重载(overload)& 重写(override) 1.1重载(overload)&#xff1a;指的是在同一个类中定义多个具有相同名称但参数列表不同的方法。通过参数列表的不同&#xff0c;编译器能够区分这些方法&#xff0c;并根据调用时传递的…