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

在上一节基础上,去除了节点的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));
    }
}

运行结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/889364.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

股指期货的杠杆是怎么体现和使用的?

股指期货的杠杆效应是通过保证金交易实现的。投资者只需支付合约价值的一小部分作为保证金&#xff0c;即可控制整个合约的价值。例如&#xff0c;如果一个股指期货合约的价值为100,000元&#xff0c;而保证金比例为10%&#xff0c;那么投资者只需支付10,000元即可控制这个合约…

SpringBoot教程(二十四) | SpringBoot实现分布式定时任务之Quartz(基础)

SpringBoot教程&#xff08;二十四&#xff09; | SpringBoot实现分布式定时任务之Quartz&#xff08;基础&#xff09; 简介适用场景Quartz核心概念Quartz 存储方式Quartz 版本类型引入相关依赖开始集成方式一&#xff1a;内存方式(MEMORY)存储实现定时任务1. 定义任务类2. 定…

从commit校验失效问题探究husky原理

一、背景 之前创建的项目&#xff0c;发现代码 commit 提交的时候没有了任何校验&#xff0c;具体表现&#xff1a; 一是 feat fix 等主题格式校验没有了二是代码 lint 不通过也能提交 尝试解决这个问题&#xff0c;并深入了解husky的实现原理&#xff0c;将相关的一些知识点…

【Vue】Vue扫盲(三)计算属性和监听器

【Vue】Vue扫盲&#xff08;一&#xff09;事件标签、事件修饰符&#xff1a;click.prevent click.stop click.stop.prevent、按键修饰符、及常用指令 【Vue】Vue扫盲&#xff08;二&#xff09;指令&#xff1a;v-for 、v-if、v-else-if、v-else、v-show 文章目录 1、 计算属…

用FPGA做一个全画幅无反相机

做一个 FPGA 驱动的全画幅无反光镜数码相机是不是觉得很酷&#xff1f; 就是上图这样。 Sitina 一款开源 35 毫米全画幅 (3624 毫米) CCD 无反光镜可换镜头相机 (MILC)&#xff0c;这个项目最初的目标是打造一款数码相机&#xff0c;将 SLR [单镜头反光] 相机转换为 DSLR [数码…

Spring事务的1道面试题

每次聊起Spring事务&#xff0c;好像很熟悉&#xff0c;又好像很陌生。本篇通过一道面试题和一些实践&#xff0c;来拆解几个Spring事务的常见坑点。 原理 Spring事务的原理是&#xff1a;通过AOP切面的方式实现的&#xff0c;也就是通过代理模式去实现事务增强。 具体过程是…

【3dgs】总结3DGS与NeRF如何重塑SLAM24年4月最新进展

【3dgs】总结3DGS与NeRF如何重塑SLAM&#xff01; 1. 摘要2. 简洁3. 背景3.1 Existing SLAM Surveys3.2 progress in Radiance Field Theory3.3.1 NeRF3.3.2 3dgs3.4 数据集 4 数据集4.1 SLAM3.1 RGB-D SLAM方法3.1.1 基于NeRF风格的RGB-D SLAM3.1.2 基于3DGS风格的 RGB-D SLAM…

opencv的相机标定与姿态解算

首先我们要知道四个重要的坐标系 世界坐标系相机坐标系图像成像坐标系图像像素坐标系 坐标系之间的转换 世界坐标系——相机坐标系 从世界坐标系到相机坐标系&#xff0c;涉及到旋转和平移&#xff08;其实所有的运动也可以用旋转矩阵和平移向量来描述&#xff09;。绕着不…

Python编程:创意爱心表白代码集

在寻找一种特别的方式来表达你的爱意吗&#xff1f;使用Python编程&#xff0c;你可以创造出独一无二的爱心图案&#xff0c;为你的表白增添一份特别的浪漫。这里为你精选了六种不同风格的爱心表白代码&#xff0c;让你的创意和情感通过代码展现出来。 话不多说&#xff0c;咱…

C++开发五子棋游戏案例详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

MSYS2+GCC 安装与应用保姆手册

msys2 提供可在Windows下使用 GCC 编译器&#xff1b;并且&#xff0c;借助 Linux 包管理功能&#xff0c;可轻松下载丰富的可在Windows下直接使用的 C/C 开发包&#xff0c;包括编译好的二进制包。 网络库asio、准标准库boost、zip解压缩、json格式处理、引擎 SDL……十八般兵…

图片美化SDK解决方案,赋能H5与小程序极致体验

无论是社交媒体分享、电商产品展示&#xff0c;还是个人日常生活的记录&#xff0c;一张经过精心美化的图片总能瞬间吸引眼球&#xff0c;传递出更加丰富和动人的信息。如何在不增加应用体积、不牺牲用户体验的前提下&#xff0c;为H5页面和小程序提供媲美原生APP的图片美化功能…

前端高频面试题2024/9/22(偏项目问题--通用后台管理系统)

文章目录 一.前端项目概述1.系统登录注册模块1.对注册的密码进行加密 &#xff08;使用加密中间件bcrypt.js&#xff09;2.登录成功后返回token3.前端登录页面有用到弹性布局&#xff0c;ref和reactive4.登录头像&#xff1a;文件上传 2.系统设置模块2.系统首页模块&#xff08…

Unity 从零开始的框架搭建1-2 事件的发布-订阅-取消的小优化及调用对象方法总结[半干货]

该文章专栏是向QFrameWork作者凉鞋老师学习总结得来&#xff0c;吃水不忘打井人&#xff0c;不胜感激 Unity 从零开始的框架搭建1-1 unity中对象调用的三种方式的优缺点分析【干货】-CSDN博客 原来 其实就是对上一节的事件发布订阅类的小优化&#xff0c;原来是这样子的 p…

【SEO】什么是SEO?

什么是SEO&#xff08;搜索引擎优化&#xff09;&#xff1f;为什么SEO对于⼀个⽹站⾄关重要&#xff1f; SEO 全称是搜索引擎优化&#xff08;Search Engine Optimization&#xff09; 因为我们目前开发的网址&#xff0c;需要人看到&#xff0c;除了通过宣传营销的方式展现…

计算机毕业设计 校内跑腿业务系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

Qt操作主/从视图及XML——实例:汽车管理系统

目录 1. 主界面布局2.连接数据库3.主/从视图应用 1. 主界面布局 先创建一个QMainwindow&#xff0c;不带设计界面 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow> #include <QGroupBox> #include <QTableView> #include <QListWidg…

【环境搭建】MAC M1安装ElasticSearch

STEP1 官网下载ES Download Elasticsearch | Elastic&#xff0c;下载mac m1对应版本的es STEP2 进入bin文件夹&#xff0c;执行./elasticSearch 浏览器输入 127.0.0.1:9200 STEP 3 下载对应Kibana版本&#xff0c;Download Kibana Free | Get Started Now | Elastic 出现报错…

Linux驱动开发(速记版)--单总线

第124章 单总线简介 124.1 单总线概述 单总线是一种串行通信协议&#xff0c;由Dallas Semiconductor开发&#xff0c;特点是用一根信号线实现双向数据传输和时钟同步&#xff0c;节省IO口且结构简单。 它广泛应用于传感器、存储器等。 硬件包括信号线、上拉电阻、设备和处理器…

高亚科技助力优巨新材,打造高效数字化研发项目管理平台

近日&#xff0c;中国企业管理软件资深服务商高亚科技与广东优巨先进新材料股份有限公司&#xff08;以下简称“优巨新材”&#xff09;正式签署合作协议&#xff0c;共同推进产品研发管理数字化升级。此次合作的主要目标是通过8Manage PM项目管理系统&#xff0c;提升优巨新材…