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

news2024/10/11 6:53:27

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

有向图示例:

在这里插入图片描述

基本思路

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

数据模型:

  1. 节点数据模型:
package com.angel.ocean.domain.dsf;

import lombok.Data;
import java.util.List;

@Data
public class ChainItem {

    // 该节点ID
    private Integer id;

    // 该节点可以到达哪些节点的ID列表
    private List<Integer> next;

    public ChainItem(Integer id, List<Integer> next) {
        this.id = id;
        this.next = next;
    }
}
  1. 有向图链路数据模型:
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;
    }
}

算法实现

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);
            // 是否为终止链路,终止链路设置为true
            if(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);
                // 是否为终止链路,终止链路设置为true
                if(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;
    }
}

算法验证

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.coloradmin.cn/o/2204455.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java控制台实现《多线程模拟龟兔赛跑》(实现Runnale接口,重写run()方法)

&#xff08;温馨提示&#xff1a;本题最重要的是学习思路&#xff0c;代码还有待优化和改进&#xff01;&#xff09; 下一篇博客进行优化。实现Callable接口&#xff1a;V call() throws Exception 。可以返回结果&#xff0c;以及可以抛出异常。&#xff08;启动线程比较麻烦…

spingboot项目打包到docker镜像[保姆级教程]

目录 1. 项目准备 2. 项目打包测试 3. docker配置 3.1 服务器端配置 3.2 开发IDEA配置 3.3 创建dockerfile 写在前面: 这博客一晃鸽了好久了,其实不是没有创作,只是懒得写博客了.日常都记录在自己的语雀小本本上了.好久没有出保姆级教程了&#xff0c;这次忽然想通过ide…

IP地址类型选择指南:动态IP、静态IP还是数据中心IP?

你是否曾经困惑于如何选择最适合业务需求的IP地址类型&#xff1f;面对动态IP、静态IP和数据中心IP这三种选择&#xff0c;你是否了解它们各自对你的跨境在线业务可能产生的深远影响&#xff1f; 在跨境电商领域&#xff0c;选择合适的IP类型对于业务的成功至关重要。动态IP、…

UART在Linux内核启动时突然不打印的问题

国庆前一天收到的任务&#xff0c;在一颗比较成熟的芯片的SDK基础上&#xff0c;移植一个新内核&#xff0c;让它能够在bitfile下跑在FPGA上。 看了芯片设计那边给的文档&#xff0c;对比过去的那颗&#xff0c;感觉也就改改寄存器&#xff0c;中断号&#xff0c;时钟&#xff…

聚焦AI|智享AI直播三代模型的出现,打破传统直播束缚!

聚焦AI|智享AI直播三代模型的出现&#xff0c;打破传统直播束缚! 在数字化浪潮的推动下&#xff0c;直播行业正经历着前所未有的变革与升级。其中&#xff0c;智享AI直播三代模型的出现&#xff0c;无疑成为了业界关注的焦点。这一创新技术不仅引发了关于无人直播未来发展方向的…

18709 魔法

### 思路 为了将所有白色奶牛排在前面&#xff0c;黑色奶牛排在后面&#xff0c;我们可以考虑两种策略&#xff1a; 1. 将所有的奶牛都变成白色。 2. 将所有的奶牛都变成黑色。 我们需要计算这两种策略所需的最少次数&#xff0c;并选择其中较小的一个。 具体步骤如下&#x…

从加载到对话:使用 Llama-cpp-python 本地运行量化 LLM 大模型(GGUF)

&#xff08;无需显卡&#xff09;使用 Llama-cpp-python 在本地加载具有 70 亿参数的 LLM 大语言模型&#xff0c;通过这篇文章你将学会用代码创建属于自己的 GPT。 建议阅读完 19a 的「前言」和「模型下载」部分后再进行本文的阅读。 代码文件下载 - Llama-cpp-python 文章目…

AI智能体:共塑企业变革新纪元,引领未来无限潜能

当我们被《银翼杀手2049》或《机械公敌》等科幻大片中那些远超人类能力、能够自主判断并行动的人工智能所震撼时&#xff0c;AI时代的“智能体”已经悄然渗透进我们的工作生活中&#xff0c;成为引领企业变革的先锋力量&#xff0c;将我们带入一个全新的纪元。 ​从辅助到共生&…

【Unity】背景图片随着背景里面内容大小而变化

今天制作项目里面的设置界面和暂停界面时&#xff0c;发现两个界面有很多重复部分&#xff0c;所以直接做一个界面就行了&#xff0c;但是两个界面的背景大小会有变化&#xff0c;图片在下面 这个是游戏暂停界面的&#xff0c;设置界面和这个界面有很多重复地方&#xff0c;仅仅…

学习博客写作

欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 新的改变 我们对Markdown编辑器进行了一些功能拓展与语法支持&#x…

安卓手机平板远程访问内网服务器中安装的code-server编程开发实战

文章目录 前言1.Ubuntu本地安装code-server2. 安装cpolar内网穿透3. 创建隧道映射本地端口4. 安卓平板测试访问5.固定域名公网地址6.结语 前言 本文主要介绍如何在Linux Ubuntu系统安装code-server&#xff0c;并结合cpolar内网穿透工具配置公网地址&#xff0c;轻松实现使用安…

SQL 干货 | 使用 Having 子句筛选聚合字段

如果你编写 SQL 查询已有一段时间&#xff0c;那么你可能对 WHERE 子句非常熟悉。虽然它对聚合字段没有影响&#xff0c;但有一种方法可以根据聚合值过滤记录&#xff0c;那就是使用 HAVING 子句。本博客将介绍它的工作原理&#xff0c;并提供几个在 SELECT 查询中使用它的示例…

计次卡魔都千丝冥缘应用———未来之窗行业应用跨平台架构

一、魔都千丝冥缘作用 在本次卡购买种&#xff0c;涉及卡包表单、次卡表单&#xff0c;商品表单&#xff0c;提成表单&#xff0c;支付方式表单&#xff0c;职员表单 并且在商品表和次卡表单字段一样&#xff0c;元素name名称一样。 未来之窗魔都千丝冥缘&#xff0c;将功能…

字节跳动青训营开始报名了!

关于青训营&#xff1a; 青训营是字节跳动技术团队发起的技术系列培训 &人才选拔项目;面向高校在校生&#xff0c;旨在培养优秀且具有职业竞争力的开发工程师。 本次技术训练营由掘金联合豆包MarsCode 团队主办课程包含前端、后端和 A 方向&#xff0c;在这个飞速发…

高频股票期货ETF历史高频数据源

【数据源】 银河金融数据库&#xff08;yinhedata.com&#xff09; 提供金融数据股票、美股、期货以及ETF等高频tick数据&#xff0c;分钟级别数据。 MACD背离是指MACD指标与价格走势之间发生的方向性差异&#xff0c;这通常被视为市场可能发生趋势反转的信号。以下是一个具体…

GS-SLAM论文阅读笔记-CG-SLAM

前言 这是一篇不是最新的工作&#xff0c;我之前没有阅读&#xff0c;但是我前几天阅读GLC-SLAM的时候&#xff0c;发现它的一部分内容参考了CG-SLAM&#xff0c;并且CG-SLAM最近被ECCV2024接收&#xff0c;说明这是一片值得参考的好文章&#xff0c;接下来就阅读一下吧&#…

数据库——表格之间的关系(表格之间的连接和处理)

数据库表格之间经常存在各种关系&#xff1a; 一对一、一对多、多对多 1.一对一 —— 丈夫表&#xff0c;妻子表为例 连接方式一&#xff1a;合并为一张表 这种方式对于一对一来说最优 连接方式二&#xff1a;在其中一张表内加入一个外键&#xff0c;连接另一张表 连…

Windows搭建Java开发环境(Building a Java development environment on Windows)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

开发日志:IIS安全配置

为了解决IIS文件路径泄漏问题&#xff0c;可以采取以下措施&#xff1a; 一. 详细操作 1. CMD关闭NTFS 8.3文件格式的支持 命令行&#xff1a;fsutil 8dot3name set 1 2. 修改注册表禁用短文件名功能 CMD输入regedit回车&#xff0c;在注册表中找到HKEY_LOCAL_MACHINE\SYSTEM\C…

003 Springboot操作RabbitMQ

Springboot整合RabbitMQ 文章目录 Springboot整合RabbitMQ1.pom依赖2.yml配置3.配置队列、交换机方式一&#xff1a;直接通过配置类配置bean方式二&#xff1a;消息监听通过注解配置 4.编写消息监听发送测试5.其他类型交换机配置1.FanoutExchange2.TopicExchange3.HeadersExcha…