无向图的最短路径算法JAVA实现(转)

简介: 一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点。求出图中所有顶点到源点的最短路径。 无向图的最短路径其实是源点到该顶点的最少边的数目。 本文假设图的信息保存在文件中,通过读取文件来构造图。

一,问题描述

给出一个无向图,指定无向图中某个顶点作为源点。求出图中所有顶点到源点的最短路径。

无向图的最短路径其实是源点到该顶点的最少边的数目。

本文假设图的信息保存在文件中,通过读取文件来构造图。文件内容的格式参考这篇文章第一部分

 

二,算法实现思路

无向图的最短路径实现相对于带权的有向图最短路径实现要简单得多。

源点的最短路径距离为0,从源点开始,采用广度优先的顺序,首先将与源点邻接的顶点的路径求出,然后再依次求解图中其他顶点的最短路径。

由于顶点的最短路径的求解顺序 是一个 广度优先的顺序,因此需要一个辅助队列。初始时,将源点的最短路径距离设置为0,将源点入队列。

然后,在一个while循环中,从队列中弹出顶点,遍历该顶点的邻接点,若该邻接点的距离未被更新过(表示该邻接点未被访问过),更新邻接点的最短路径距离为 该顶点的距离加上1,并将所有的邻接点入队列。

 

三,最短路径算法的实现

感觉该算法的实现与 二叉树的层序遍历,有向图的拓扑排序算法实现都非常的相似。他们都采用了广度的思想在里面。

广度优先的思想就是:处理完某个顶点后,去处理该顶点的所有邻接点,处理完它的邻接点后,再去处理更远(更外层)的顶点。

算法的代码如下:

复制代码
 1     /*
 2      * 计算源点s到无向图中各个顶点的最短路径
 3      * 需要一个队列来保存图中的顶点,初始时,源点入队列,然后以广度的形式向外扩散求解其他顶点的最短路径
 4      */
 5     private void unweightedShortestPath(Vertex s){
 6         //初始化
 7         Queue<Vertex> queue = new LinkedList<>();
 8         s.dist = 0;
 9         queue.offer(s);//将源点dist设置为0并入队列
10         
11         while(!queue.isEmpty()){
12             Vertex v = queue.poll();
13             for (Edge e : v.adjEdges) {//扫描v的邻接边(点)
14                 if(e.endVertex.dist == Integer.MAX_VALUE){//如果这个顶点(e.endVertex)未被访问(每个顶点只会入队列一次)
15                     e.endVertex.dist = v.dist + 1;//更新该顶点到源点的距离
16                     queue.offer(e.endVertex);
17                     e.endVertex.preNode = v;//设置该顶点的前驱顶点
18                 }//end if
19             }//end for
20         }//end while
21     }
复制代码

第11行while循环,每个顶点出队列一次,第13行for循环,表示每条边被处理一次,故算法的时间复杂度为O(V+E)

第14行if语句表明,图中每个顶点只会入队列一次。因为,顶点入队列后,该顶点的 dist 设置为 v.dist+1,不再是 Integer.MAX_VALUE

 

四,完整代码实现

NonDirectedGraph.java构造图并实现最短路径算法

复制代码
  1 import java.util.Collection;
  2 import java.util.LinkedHashMap;
  3 import java.util.LinkedList;
  4 import java.util.List;
  5 import java.util.Map;
  6 import java.util.Queue;
  7 
  8 /*
  9  * 求解无向图的单源最短路径
 10  */
 11 public class NonDirectedGraph {
 12     private class Vertex{
 13         private String vertexLabel;//顶点标识
 14         private List<Edge> adjEdges;//与该顶点邻接的边(点)
 15         private int dist;//顶点距离
 16         private Vertex preNode;
 17         
 18         public Vertex(String vertexLabel) {
 19             this.vertexLabel = vertexLabel;
 20             adjEdges = new LinkedList<>();
 21             dist = Integer.MAX_VALUE;
 22             preNode = null;
 23         }
 24     }
 25     private class Edge{
 26         private Vertex endVertex;
 27         public Edge(Vertex endVertex) {
 28             this.endVertex = endVertex;
 29         }
 30     }
 31     
 32     private Map<String, Vertex> nonDirectedGraph;
 33     private Vertex startVertex;//图的起始顶点
 34     
 35     public NonDirectedGraph(String graphContent) {
 36         nonDirectedGraph = new LinkedHashMap<>();
 37         buildGraph(graphContent);
 38     }
 39     
 40     private void buildGraph(String graphContent){
 41         String[] lines = graphContent.split("\n");
 42         
 43         String startNodeLabel, endNodeLabel;
 44         Vertex startNode, endNode;
 45         for(int i = 0; i < lines.length; i++){
 46             String[] nodesInfo = lines[i].split(",");
 47             startNodeLabel = nodesInfo[1];
 48             endNodeLabel = nodesInfo[2];
 49             
 50             endNode = nonDirectedGraph.get(endNodeLabel);
 51             if(endNode == null){
 52                 endNode = new Vertex(endNodeLabel);
 53                 nonDirectedGraph.put(endNodeLabel, endNode);
 54             }
 55             
 56             startNode = nonDirectedGraph.get(startNodeLabel);
 57             if(startNode == null){
 58                 startNode = new Vertex(startNodeLabel);
 59                 nonDirectedGraph.put(startNodeLabel, startNode);
 60             }
 61             Edge e = new Edge(endNode);
 62             //对于无向图而言,起点和终点都要添加边
 63             endNode.adjEdges.add(e);
 64             startNode.adjEdges.add(e);
 65         }
 66         startVertex = nonDirectedGraph.get(lines[0].split(",")[1]);//总是以文件中第一行第二列的那个标识顶点作为源点
 67     }
 68     
 69     public void unweightedShortestPath(){
 70         unweightedShortestPath(startVertex);
 71     }
 72     
 73     
 74     /*
 75      * 计算源点s到无向图中各个顶点的最短路径
 76      * 需要一个队列来保存图中的顶点,初始时,源点入队列,然后以广度的形式向外扩散求解其他顶点的最短路径
 77      */
 78     private void unweightedShortestPath(Vertex s){
 79         //初始化
 80         Queue<Vertex> queue = new LinkedList<>();
 81         s.dist = 0;
 82         queue.offer(s);//将源点dist设置为0并入队列
 83         
 84         while(!queue.isEmpty()){
 85             Vertex v = queue.poll();
 86             for (Edge e : v.adjEdges) {//扫描v的邻接边(点)
 87                 if(e.endVertex.dist == Integer.MAX_VALUE){//如果这个顶点(e.endVertex)未被访问(每个顶点只会入队列一次)
 88                     e.endVertex.dist = v.dist + 1;//更新该顶点到源点的距离
 89                     queue.offer(e.endVertex);
 90                     e.endVertex.preNode = v;//设置该顶点的前驱顶点
 91                 }//end if
 92             }//end for
 93         }//end while
 94     }
 95     
 96     //打印图中所有顶点到源点的距离及路径
 97     public void showDistance(){
 98         Collection<Vertex> vertexs = nonDirectedGraph.values();
 99         for (Vertex vertex : vertexs) {
100             System.out.print(vertex.vertexLabel + "<--");
101             Vertex tmpPreNode = vertex.preNode;
102             while(tmpPreNode != null){
103                 System.out.print(tmpPreNode.vertexLabel + "<--");
104                 tmpPreNode = tmpPreNode.preNode;
105             }
106             System.out.println("distance=" + vertex.dist);
107         }
108     }
109 }
复制代码

打印路径也可以使用递归来实现:

1     public void showDistanceRecursive(Vertex v){
2         if(v.preNode != null){
3             showDistanceRecursive(v.preNode);
4         }
5         System.out.print(v.vertexLabel + "  ");
6     }

打印顶点 v 的路径,第三行 先打印 v 的前驱顶点的路径,然后再在第5行打印 v 。

第5行的打印输出语句在第三行的递归调用语句之后,故最里层的递归调用最先被打印出来,最里层的递归调用即源点,因为只有源点的 preNode == null。

当所有的里层递归调用返回后,最终执行到最外层的递归调用处,执行第5行打印 顶点 v 后,整个递归结束。

 

TestShortestPath.java是个测试类,用来测试结果。

复制代码
 1 public class TestShortestPath {
 2     public static void main(String[] args) {
 3         String graphFilePath;
 4         if(args.length == 0)
 5             graphFilePath = "F:\\xxx";
 6         else
 7             graphFilePath = args[0];
 8         
 9         String graphContent = FileUtil.read(graphFilePath, null);
10         NonDirectedGraph graph = new NonDirectedGraph(graphContent);
11         graph.unweightedShortestPath();
12         graph.showDistance();
13     }
14 }
复制代码

 

FileUtil.java负责读取存储图信息的文件。具体参考 有向图的拓扑排序算法JAVA实现

保存图的 文件内容如下:

0,0,1,4
1,0,2,7
2,0,3,3
3,1,2,3
4,1,4,2
5,3,4,3
6,2,5,2
7,4,5,2

 

测试输出结果如下:

源点标识是 0,

0 号顶点到 1 号顶点的最短距离为1,路径为:0-->1

0 号顶点到 5 号顶点的最短距离为2,路径为:0-->2-->5

.....

....

 

http://www.cnblogs.com/hapjin/p/5435724.html

 

目录
相关文章
|
11月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
设计模式 算法 搜索推荐
Java 设计模式之策略模式:灵活切换算法的艺术
策略模式通过封装不同算法并实现灵活切换,将算法与使用解耦。以支付为例,微信、支付宝等支付方式作为独立策略,购物车根据选择调用对应支付逻辑,提升代码可维护性与扩展性,避免冗长条件判断,符合开闭原则。
1328 35
|
11月前
|
存储 缓存 监控
上网行为监控系统剖析:基于 Java LinkedHashMap 算法的时间序列追踪机制探究
数字化办公蓬勃发展的背景下,上网行为监控系统已成为企业维护信息安全、提升工作效能的关键手段。该系统需实时记录并深入分析员工的网络访问行为,如何高效存储和管理这些处于动态变化中的数据,便成为亟待解决的核心问题。Java 语言中的LinkedHashMap数据结构,凭借其独有的有序性特征以及可灵活配置的淘汰策略,为上网行为监控系统提供了一种兼顾性能与功能需求的数据管理方案。本文将对LinkedHashMap在上网行为监控系统中的应用原理、实现路径及其应用价值展开深入探究。
241 3
|
6月前
|
存储 算法 搜索推荐
《数据之美》:Java数据结构与算法精要
本系列深入探讨数据结构与算法的核心原理及Java实现,涵盖线性与非线性结构、常用算法分类、复杂度分析及集合框架应用,助你提升程序效率,掌握编程底层逻辑。
|
11月前
|
人工智能 算法 NoSQL
LRU算法的Java实现
LRU(Least Recently Used)算法用于淘汰最近最少使用的数据,常应用于内存管理策略中。在Redis中,通过`maxmemory-policy`配置实现不同淘汰策略,如`allkeys-lru`和`volatile-lru`等,采用采样方式近似LRU以优化性能。Java中可通过`LinkedHashMap`轻松实现LRUCache,利用其`accessOrder`特性和`removeEldestEntry`方法完成缓存淘汰逻辑,代码简洁高效。
501 0
|
6月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
10月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
637 58
|
9月前
|
机器学习/深度学习 算法 Java
Java实现林火蔓延路径算法
记录正在进行的森林防火项目中林火蔓延功能,本篇文章可以较好的实现森林防火蔓延,但还存在很多不足,如:很多参数只能使用默认值,所以蔓延范围仅供参考。(如果底层设备获取的数据充足,那当我没说)。注:因林火蔓延涉及因素太多,如静可燃物载量、矿质阻尼系数等存在估值,所以得出的结果仅供参考。
354 5
|
9月前
|
存储 负载均衡 算法
我们来说一说 Java 的一致性 Hash 算法
我是小假 期待与你的下一次相遇 ~
451 1
|
8月前
|
运维 监控 算法
基于 Java 滑动窗口算法的局域网内部监控软件流量异常检测技术研究
本文探讨了滑动窗口算法在局域网流量监控中的应用,分析其在实时性、资源控制和多维分析等方面的优势,并提出优化策略,结合Java编程实现高效流量异常检测。
324 0