一,问题描述

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

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

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

二,算法实现思路

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

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

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

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

三,最短路径算法的实现

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

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

算法的代码如下:

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

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

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

四,完整代码实现

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

 import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue; /*
* 求解无向图的单源最短路径
*/
public class NonDirectedGraph {
private class Vertex{
private String vertexLabel;//顶点标识
private List<Edge> adjEdges;//与该顶点邻接的边(点)
private int dist;//顶点距离(该顶点到起始顶点的距离)
private Vertex preNode; public Vertex(String vertexLabel) {
this.vertexLabel = vertexLabel;
adjEdges = new LinkedList<>();
dist = Integer.MAX_VALUE;
preNode = null;
}
}
private class Edge{
private Vertex endVertex;
public Edge(Vertex endVertex) {
this.endVertex = endVertex;
}
} private Map<String, Vertex> nonDirectedGraph;//保存了图中所有的顶点,边的关系以List形式保存在Vertex类中
private Vertex startVertex;//图的起始顶点 public NonDirectedGraph(String graphContent) {
nonDirectedGraph = new LinkedHashMap<>();
buildGraph(graphContent);
} private void buildGraph(String graphContent){
String[] lines = graphContent.split("\n"); String startNodeLabel, endNodeLabel;
Vertex startNode, endNode;
for(int i = 0; i < lines.length; i++){
String[] nodesInfo = lines[i].split(",");
startNodeLabel = nodesInfo[1];
endNodeLabel = nodesInfo[2]; endNode = nonDirectedGraph.get(endNodeLabel);
if(endNode == null){
endNode = new Vertex(endNodeLabel);
nonDirectedGraph.put(endNodeLabel, endNode);
} startNode = nonDirectedGraph.get(startNodeLabel);
if(startNode == null){
startNode = new Vertex(startNodeLabel);
nonDirectedGraph.put(startNodeLabel, startNode);
}
Edge e = new Edge(endNode);
//对于无向图而言,起点和终点都要添加边
endNode.adjEdges.add(e);
startNode.adjEdges.add(e);
}
startVertex = nonDirectedGraph.get(lines[0].split(",")[1]);//总是以文件中第一行第二列的那个标识顶点作为源点
} public void unweightedShortestPath(){
unweightedShortestPath(startVertex);
} /*
* 计算源点s到无向图中各个顶点的最短路径
* 需要一个队列来保存图中的顶点,初始时,源点入队列,然后以广度的形式向外扩散求解其他顶点的最短路径
*/
private void unweightedShortestPath(Vertex s){
//初始化
Queue<Vertex> queue = new LinkedList<>();
s.dist = 0;
queue.offer(s);//将源点dist设置为0并入队列 while(!queue.isEmpty()){
Vertex v = queue.poll();
for (Edge e : v.adjEdges) {//扫描v的邻接边(点)
if(e.endVertex.dist == Integer.MAX_VALUE){//如果这个顶点(e.endVertex)未被访问(每个顶点只会入队列一次)
e.endVertex.dist = v.dist + 1;//更新该顶点到源点的距离
queue.offer(e.endVertex);
e.endVertex.preNode = v;//设置该顶点的前驱顶点
}//end if
}//end for
}//end while
} //打印图中所有顶点到源点的距离及路径
public void showDistance(){
Collection<Vertex> vertexs = nonDirectedGraph.values();
for (Vertex vertex : vertexs) {
System.out.print(vertex.vertexLabel + "<--");
Vertex tmpPreNode = vertex.preNode;
while(tmpPreNode != null){
System.out.print(tmpPreNode.vertexLabel + "<--");
tmpPreNode = tmpPreNode.preNode;
}
System.out.println("distance=" + vertex.dist);
}
}
}

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

     public void showDistanceRecursive(Vertex v){
if(v.preNode != null){
showDistanceRecursive(v.preNode);
}
System.out.print(v.vertexLabel + " ");
}

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

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

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

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

 public class TestShortestPath {//hapjin test
public static void main(String[] args) {
String graphFilePath;
if(args.length == 0)
graphFilePath = "F:\\xxx";
else
graphFilePath = args[0]; String graphContent = FileUtil.read(graphFilePath, null);
NonDirectedGraph graph = new NonDirectedGraph(graphContent);
graph.unweightedShortestPath();
graph.showDistance();
}
}

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

.....

....

无向图的最短路径算法JAVA实现的更多相关文章

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

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

  2. 加权图的最小生成树、最短路径算法 - java实现

    加权图相关算法 前言 本文主要介绍加权图算法中两个重要应用:最小生成树和最短路径. 求最小生成树时针对的是加权无向图,加权有向图的最小生成树算法成为"最小属树形图"问题,较为复杂, ...

  3. 带权图的最短路径算法(Dijkstra)实现

    一,介绍 本文实现带权图的最短路径算法.给定图中一个顶点,求解该顶点到图中所有其他顶点的最短路径 以及 最短路径的长度.在决定写这篇文章之前,在网上找了很多关于Dijkstra算法实现,但大部分是不带 ...

  4. 有向图的拓扑排序算法JAVA实现

    一,问题描述 给定一个有向图G=(V,E),将之进行拓扑排序,如果图有环,则提示异常. 要想实现图的算法,如拓扑排序.最短路径……并运行看输出结果,首先就得构造一个图.由于构造图的方式有很多种,这里假 ...

  5. 最短路径算法之Dijkstra算法(java实现)

    前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是“贪心算法”的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知 ...

  6. Java邻接表表示加权有向图,附dijkstra最短路径算法

    从A到B,有多条路线,要找出最短路线,应该用哪种数据结构来存储这些数据. 这不是显然的考查图论的相关知识了么, 1.图的两种表示方式: 邻接矩阵:二维数组搞定. 邻接表:Map<Vertext, ...

  7. 最短路径算法-Dijkstra算法的应用之单词转换(词梯问题)(转)

    一,问题描述 在英文单词表中,有一些单词非常相似,它们可以通过只变换一个字符而得到另一个单词.比如:hive-->five:wine-->line:line-->nine:nine- ...

  8. 最短路径算法(Dijkstra算法、Floyd-Warshall算法)

    最短路径算法具体的形式包括: 确定起点的最短路径问题:即已知起始结点,求最短路径的问题.适合使用Dijkstra算法. 确定终点的最短路径问题:即已知终结结点,求最短路径的问题.在无向图中,该问题与确 ...

  9. 最短路径算法——Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson

    根据DSqiu的blog整理出来 :http://dsqiu.iteye.com/blog/1689163 PS:模板是自己写的,如有错误欢迎指出~ 本文内容框架: §1 Dijkstra算法 §2 ...

随机推荐

  1. [moka同学笔记]YII2中发送邮件示例(转载)

    原文:http://yiilib.com/topic/675/Yii2%E4%B8%AD%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6%E7%A4%BA%E4%BE%8B { ...

  2. AI - Ideas

    Idea: Comprehend code and re-factory code to more readable. The AI program can comprehend source cod ...

  3. python函数和常用模块(二),Day4

    内置函数2 装饰器 字符串格式化 生成器 迭代器 递归 模块 序列化相关 time模块 datetime模块 内置函数2 callable() # 是否可以被执行,是否可以被调用 chr() # as ...

  4. winform 属性

    WinForm为客户端程序必须在.NET Framework框架上运行 一.常用属性: 布局: AutoScroll:当控件内容超出可见区域是否显示滚动条: Autosize:当控件内容有超出时是否自 ...

  5. chenxi的html学习笔记

    0.本文主体源自:http://www.cnblogs.com/coco1s/p/4034937.html,有兴趣的可以直接去那里看,也可以看看我整理加拓展的.1.浏览器内核: 1.ie:triden ...

  6. 关于SAP的视图类型

    1 sap的视图的类型sap的视图的类型有五种 Database views (数据库视图):和数据库的视图形同,连接条件是必须自定义. Projection views(投影视图): 用于屏蔽一些字 ...

  7. oracle10g 统计信息查看、收集

      1. 统计信息查看 1.1 单个表的全局统计信息.统计效果查看 2. 统计信息分析(收集) 2.1 分析工具选择 2.2 分析前做index重建 2.3 分析某数据表,可以在PL/SQL的comm ...

  8. Configure SSL for SharePoint 2013

    http://blogs.msdn.com/b/fabdulwahab/archive/2013/01/21/configure-ssl-for-sharepoint-2013.aspx In thi ...

  9. String.format() 格式化字符串

    1.几种常见的转换符 转换符 说明 实例 %d 整数类型(十进制) 99 %f 浮点类型 99.99 %s 字符串类型 "mingrisoft" %c 字符类型 'm' %b 布尔 ...

  10. 二叉堆(binary heap)

    堆(heap) 亦被称为:优先队列(priority queue),是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因 ...