无向图中寻找指定路径:深度优先遍历算法

刷题记录

1. 节点依赖

背景: 类似于无向图中, 寻找从 起始节点 --> 目标节点 的 线路.

需求: 现在需要从 起始节点 A, 找到所有到 终点 H 的所有路径

A – B : 路径由一个对象构成

public class NodeAssociation {

    private String leftNodeName;

    private String rightNodeName;
}

A – B 可以是:

{ leftNodeName = A , rightNodeName = B }

或者

{ leftNodeName = B , rightNodeName = A }

在这里插入图片描述

找到 A --> E 的所有路径:

以及

找到 A --> D 的所有路径:

【注意】

  1. 可能存在 A --B , B – A 这种干扰项
  2. 可能存在 A – A 这种干扰项
【思路】

深度优先遍历算法解决

这个就是一个典型的无向图中求 路径的问题; 

从A 开始找到 A的邻接点列表, 然后选择其中一个结点, 再依次选择其下一个结点, 再找到其邻接点列表,再从列表中选择一个访问, 再访问其下一个结点, 直到找到E为止.

具体: 
从 A 开始, 
	A 的邻接列表 [B, C, D]
访问 B , B不是目标节点,继续向下~  
	B 的邻接列表 [E]
访问 E, E是目标, 存储路径 (A-B-E)

然后回退到A, 访问 A的邻接列表的第2个节点 C ..... 
具体代码实现
@Data
public class Solution {

    // 原始数据
    private List<NodeAssociation> rawData;

    // 测试
        public static void main(String[] args) {
        Solution solution = new Solution();
        NodeAssociation nodeAssociation1 = new NodeAssociation("A",  "B");
        NodeAssociation nodeAssociation2 = new NodeAssociation("A",  "C" );
        NodeAssociation nodeAssociation3 = new NodeAssociation("A",  "D" );
        NodeAssociation nodeAssociation4 = new NodeAssociation("B",  "E" );
        NodeAssociation nodeAssociation5 = new NodeAssociation("E",  "C" );
        NodeAssociation nodeAssociation6 = new NodeAssociation("E",  "H" );
        NodeAssociation nodeAssociation7 = new NodeAssociation("D",  "F" );
        NodeAssociation nodeAssociation8 = new NodeAssociation("H",  "F" );
        NodeAssociation nodeAssociation9 = new NodeAssociation("D",  "A" ); // 干扰项
        NodeAssociation nodeAssociation10 = new NodeAssociation("C",  "F" );

        ArrayList<NodeAssociation> list = new ArrayList<>();
        list.add(nodeAssociation1);
        list.add(nodeAssociation2);
        list.add(nodeAssociation3);
        list.add(nodeAssociation4);
        list.add(nodeAssociation5);
        list.add(nodeAssociation6);
        list.add(nodeAssociation7);
        list.add(nodeAssociation8);
        list.add(nodeAssociation9);
        list.add(nodeAssociation10);

        solution.setRawData(list);
        System.out.println("打印原始数据");
        for (NodeAssociation rawDatum : solution.rawData) {
            System.out.println(rawDatum);
        }
        solution.find("A", "E", "D");
    }

    public void find(String rootNodeName, String target1, String target2) {
        // 1. 对模型对去重 (A-B) (B-A) --> (A-B);  (A-A)无效节点直接删除
        List<String> tempList = new ArrayList<>();
        for (int i = rawData.size() - 1; i >= 0; i--) {
            NodeAssociation nodeAssociation = rawData.get(i);
            String leftNodeName = nodeAssociation.getLeftNodeName();
            String rightNodeName = nodeAssociation.getRightNodeName();
            // 若有重复,则去重 (A-B) (B-A) --> (A-B)
            if (tempList.contains(leftNodeName + rightNodeName) || tempList.contains(rightNodeName + leftNodeName)) {
                rawData.remove(i);
                continue;
            }
            // (A-A) 直接删除
            if (leftNodeName.equals(rightNodeName)){
                rawData.remove(i);
                continue;
            }
            tempList.add(leftNodeName + rightNodeName);
        }

        // 2. 从索引模型开始遍历
        List<String> roadList = new ArrayList<>();
        roadList.add(rootNodeName);
        List<NodeAssociation> roadNodeList = new ArrayList<>();
        // 2.1 找到 target1
        List<List<NodeAssociation>> result1 = new ArrayList<>();
        dfs(result1, roadList, roadNodeList, rootNodeName, target1);
        System.out.println("打印  "+rootNodeName+"  -->  "+target1+"结果集:" );
        for (int i = 0; i < result1.size(); i++) {
            System.out.println("打印第"+(i+1)+"条路:");
            for (NodeAssociation nodeAssociation : result1.get(i)) {
                System.out.println(nodeAssociation);
            }
        }

        // 2.2 找到 target2
        List<List<NodeAssociation>> result2 = new ArrayList<>();
        dfs(result2, roadList, roadNodeList, rootNodeName, target2);
        System.out.println("打印  "+rootNodeName+"  -->  "+target2+"结果集:" );
        for (int i = 0; i < result2.size(); i++) {
            System.out.println("打印第"+(i+1)+"条路:");
            for (NodeAssociation nodeAssociation : result2.get(i)) {
                System.out.println(nodeAssociation);
            }
        }
    }


    // 1. 当前走过的路
    public void dfs(List<List<NodeAssociation>> finalList, List<String> roadList, List<NodeAssociation> roadNodeList, String startName, String targetName) {
        // 0. 对数据处理 - 放在上一层方法中.

        // 1. 边界条件: ① 如果 开始节点没有子节点 可以结束 ② 该节点为 目标节点, 则对list进行结果的收取 并返回
        // 1.1 判断该节点是否为目标节点:
        if (startName.equals(targetName)) {
            // 拷贝
            List<NodeAssociation> result = new ArrayList<>(roadNodeList);
            // 结果收割
            finalList.add(result);
            return;
        }
        // 1.2 如果不是目标节点,则需要判断有没有子节点, 从原始数据中
        List<NodeAssociation> sonNodeList = findSonNodeList(rawData, startName);
        // 对子节点list进行过滤,过滤掉已经走过的路的结点
        List<NodeAssociation> collect = sonNodeList.stream().filter((item) -> {
            String leftNodeName = item.getLeftNodeName();
            String rightNodeName = item.getRightNodeName();
            String nextNode = startName.equals(leftNodeName) ? rightNodeName : leftNodeName;
            return !roadList.contains(nextNode);
        }).collect(Collectors.toList());

        if (collect.size() == 0) {
            return;
        }

        // 3. 开始遍历--> 横向遍历
        for (int i = 0; i < collect.size(); i++) {
            // 3.1 把当前遍历节点加入路径
            NodeAssociation curNode = collect.get(i);
            String leftNodeName = curNode.getLeftNodeName();
            String rightNodeName = curNode.getRightNodeName();
            String nextNode = startName.equals(leftNodeName) ? rightNodeName : leftNodeName;
            roadList.add(nextNode);
            roadNodeList.add(curNode);

            // 3.2 开始遍历递归
            dfs(finalList, roadList, roadNodeList, nextNode, targetName);

            // 3.3 回溯
            roadList.remove(roadList.size() - 1);
            roadNodeList.remove(roadNodeList.size() - 1);
        }
    }

    // 找到子节点list
    private List<NodeAssociation> findSonNodeList(List<NodeAssociation> data, String startName) {
        return data.stream().filter((item) -> startName.equals(item.getLeftNodeName()) || startName.equals(item.getRightNodeName())).collect(Collectors.toList());
    }
}

最后的结果:

打印原始数据
NodeAssociation{leftNodeName='A', rightNodeName='B'}
NodeAssociation{leftNodeName='A', rightNodeName='C'}
NodeAssociation{leftNodeName='A', rightNodeName='D'}
NodeAssociation{leftNodeName='B', rightNodeName='E'}
NodeAssociation{leftNodeName='E', rightNodeName='C'}
NodeAssociation{leftNodeName='E', rightNodeName='H'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
NodeAssociation{leftNodeName='H', rightNodeName='F'}
NodeAssociation{leftNodeName='D', rightNodeName='A'}
NodeAssociation{leftNodeName='C', rightNodeName='F'}
打印  A  -->  E结果集:
打印第1条路:
NodeAssociation{leftNodeName='A', rightNodeName='B'}
NodeAssociation{leftNodeName='B', rightNodeName='E'}
打印第2条路:
NodeAssociation{leftNodeName='A', rightNodeName='C'}
NodeAssociation{leftNodeName='E', rightNodeName='C'}
打印第3条路:
NodeAssociation{leftNodeName='A', rightNodeName='C'}
NodeAssociation{leftNodeName='C', rightNodeName='F'}
NodeAssociation{leftNodeName='H', rightNodeName='F'}
NodeAssociation{leftNodeName='E', rightNodeName='H'}
打印第4条路:
NodeAssociation{leftNodeName='D', rightNodeName='A'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
NodeAssociation{leftNodeName='H', rightNodeName='F'}
NodeAssociation{leftNodeName='E', rightNodeName='H'}
打印第5条路:
NodeAssociation{leftNodeName='D', rightNodeName='A'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
NodeAssociation{leftNodeName='C', rightNodeName='F'}
NodeAssociation{leftNodeName='E', rightNodeName='C'}

打印  A  -->  D结果集:
打印第1条路:
NodeAssociation{leftNodeName='A', rightNodeName='B'}
NodeAssociation{leftNodeName='B', rightNodeName='E'}
NodeAssociation{leftNodeName='E', rightNodeName='C'}
NodeAssociation{leftNodeName='C', rightNodeName='F'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
打印第2条路:
NodeAssociation{leftNodeName='A', rightNodeName='B'}
NodeAssociation{leftNodeName='B', rightNodeName='E'}
NodeAssociation{leftNodeName='E', rightNodeName='H'}
NodeAssociation{leftNodeName='H', rightNodeName='F'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
打印第3条路:
NodeAssociation{leftNodeName='A', rightNodeName='C'}
NodeAssociation{leftNodeName='E', rightNodeName='C'}
NodeAssociation{leftNodeName='E', rightNodeName='H'}
NodeAssociation{leftNodeName='H', rightNodeName='F'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
打印第4条路:
NodeAssociation{leftNodeName='A', rightNodeName='C'}
NodeAssociation{leftNodeName='C', rightNodeName='F'}
NodeAssociation{leftNodeName='D', rightNodeName='F'}
打印第5条路:
NodeAssociation{leftNodeName='D', rightNodeName='A'}

如果想要得到最短路径, 那么也很简单, 在每次收割结果时,对list做一下判断即可, 每次取最小的list, 便可得到最短路径。

欢迎讨论~~~

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

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

相关文章

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码

文华财经盘立方期货通鳄鱼指标公式均线交易策略源码&#xff1a; 新建主图幅图类型指标都可以&#xff01; VAR1:(HL)/2; 唇:REF(SMA(VAR1,5,1),3),COLORGREEN; 齿:REF(SMA(VAR1,8,1),5),COLORRED; 颚:REF(SMA(VAR1,13,1),8),COLORBLUE;

离线查询+线段树,CF522D - Closest Equals

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 522D - Closest Equals 二、解题报告 1、思路分析 考虑查询区间已经给出&#xff0c;我们可以离线查询 对于这类区间离线查询的问题我们通常可以通过左端点排序&#xff0c;然后遍历询问同时维护左区间信息…

数据泄露态势(2024年5月)

监控说明&#xff1a;以下数据由零零信安0.zone安全开源情报系统提供&#xff0c;该系统监控范围包括约10万个明网、深网、暗网、匿名社交社群威胁源。在进行抽样事件分析时&#xff0c;涉及到我国的数据不会选取任何政府、安全与公共事务的事件进行分析。如遇到影响较大的伪造…

《金山 WPS AI 2.0:重塑办公未来的智能引擎》

AITOP100平台获悉&#xff0c;在 2024 世界人工智能大会这一科技盛宴上&#xff0c;金山办公以其前瞻性的视野和创新的技术&#xff0c;正式发布了 WPS AI 2.0&#xff0c;犹如一颗璀璨的星辰&#xff0c;照亮了智能办公的新征程&#xff0c;同时首次公开的金山政务办公模型 1.…

支持图片识别语音输入的LobeChat保姆级本地部署流程

文章目录 前言1. LobeChat对我们有哪些帮助?2. 本地安装LobeChat3. 如何使用LobeChat工具4. 安装Cpolar内网穿透5. 实现公网访问LobeChat6. 固定LobeChat公网地址 前言 本文主要介绍如何在Windows系统电脑本地部署LobeChat&#xff0c;一款高颜值的开源AI大模型智能应用&…

5-google::protobuf命名空间下常用的C++ API----message.h

#include <google/protobuf/message.h> namespace google::protobuf 假设您有一个消息定义为: message Foo {optional string text 1;repeated int32 numbers 2; } 然后&#xff0c;如果你使用 protocol编译器从上面的定义生成一个类&#xff0c;你可以这样使用它: …

Studying-代码随想录训练营day31| 56.合并区间、738.单调递增的数字、968.监控二叉树、贪心算法总结

第31天&#xff0c;贪心最后一节(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 56.合并区间 738.单调递增的数字 968.监控二叉树 贪心算法总结 56.合并区间 文档讲解&#xff1a;代码随想录合并区间 视频讲解&#xff1a;手撕合并区间 题目&#xf…

C语言下结构体、共用体、枚举类型的讲解

主要内容 结构体结构体数组结构体指针包含结构体的结构链表链表相关操作共用体枚举类型 结构体 结构体的类型的概念 结构体实现步骤 结构体变量的声明 struct struct 结构体名{ 数据类型 成员名1; 数据类型 成员名2; ..…

绝地求生PUBG兰博基尼怎么兑换 兰博基尼怎么获得

绝地求生采用虚幻4引擎制作&#xff0c;玩家们会在一个偏远的岛屿上出生&#xff0c;然后展开一场赢家通吃的生存竞赛&#xff0c;最后只会有1个人存活。当然&#xff0c;和其他生存游戏一样&#xff0c;玩家需要在广袤复杂的地图中收集武器、车辆、物资&#xff0c;而且也会有…

解决win10报“无法加载文件……profile.ps1,因为在此系统上禁止运行脚本”的问题

打开命令行报错 解决方法 使用管理员权限打开PowerShell&#xff1a;WinX, 选择“Windows PowerShell&#xff08;管理员&#xff09;” 输入&#xff1a;Set-ExecutionPolicy -ExecutionPolicy RemoteSigned 输入&#xff1a;y确认修改安全策略 &#xff1a;y确认修改安全策略…

探讨大数据在视频汇聚平台LntonCVS国标GB28181协议中的应用

随着摄像头和视频监控系统的普及和数字化程度的提高&#xff0c;视频监控系统产生的数据量急剧增加。大数据技术因其优秀的数据管理、分析和利用能力&#xff0c;成为提升视频监控系统效能和价值的重要工具。 大数据技术可以将视频监控数据与其他数据源进行融合分析&#xff0c…

【elasticsearch】IK分词器添加自定义词库,然后更新现有的索引

进入elasticsearch中的plugins位置&#xff0c;找到ik分词器插件&#xff0c;进入ik插件的config文件夹&#xff0c;当中有一个IKAnalyzer.cfg.xml配置文件。使用vim编辑器修改配置文件&#xff1a; vim IKAnalyzer.cfg.xml 配置文件如下&#xff08;添加了自定义字典的位置&…

pygame 音乐粒子特效

代码 import pygame import numpy as np import pymunk from pymunk import Vec2d import random import librosa import pydub# 初始化pygame pygame.init()# 创建屏幕 screen pygame.display.set_mode((1920*2-10, 1080*2-10)) clock pygame.time.Clock()# 加载音乐文件 a…

人工智能的新时代:从模型到应用的转变

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

信息技术课堂上如何有效防止学生玩游戏?

防止学生在信息技术课堂上玩游戏需要综合运用教育策略和技术手段。以下是一些有效的措施&#xff0c;可以用来阻止或减少学生在课堂上玩游戏的行为&#xff1a; 1. 明确课堂规则 在课程开始之初&#xff0c;向学生清楚地说明课堂纪律&#xff0c;强调不得在上课时间玩游戏。 制…

使用tcpdump抓取本本机的所有icmp包

1、抓取本机所有icmp包 tcpdump -i any icmp -vv 图中上半部分&#xff0c;是源主机tmp179无法ping通目标主机192.168.10.79&#xff08;因为把该主机关机了&#xff09;的状态&#xff0c;注意看&#xff0c;其中有unreachable 图中下半部分&#xff0c;是源主机tmp179可以p…

Docker总结

准备环境&#xff1a; VMware17Ubuntu18.04(LTS)&#xff1a;https://releases.ubuntu.com/18.04/ubuntu-18.04.6-desktop-amd64.iso 1. Docker前瞻 docker相关文档&#xff1a; docker官网地址 : https://www.docker.com/docker文档地址 : https://docs.docker.com/docker镜像…

tomcat的优化和tomcat和nginx实现动静分离:

tomcat的优化 tomcat自身的优化 tomcat的并发处理能力不强。大项目不使用tomcat做为转发动态的中间件&#xff08;k8s集群&#xff0c;python&#xff0c;rubby&#xff09;&#xff0c;小项目会使用&#xff08;内部使用&#xff09;&#xff0c;动静分离。 优化tomcat的启动…

【Spring Boot】Spring AOP动态代理,以及静态代理

目录 Spring AOP代理一. 代理的概念二. 静态代理三. JDK代理3.1 重写 invoke 方法进⾏功能增强3.2 通过Proxy类随机生成代理对象 四. CGLIB代理4.1 自定义类来重写intercept方法4.2 通过Enhancer类的create方法来创建代理类 五. AOP源码剖析 总结(重中之重&#xff0c;精华) Sp…

一款简单、免费的web文件共享服务器

#共享文件# #远程访问# #手机访问# 文件共享已成为我们日常生活和工作中不可或缺的一部分。它如同一条无形的纽带&#xff0c;将人们紧密地联系在一起&#xff0c;促进了信息的快速传播和交流。 文件共享的魅力在于其打破了地域和时间的限制。无论我们身处世界的哪个角落&…