无向图的最短路径算法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

时间: 2024-10-15 15:23:54

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

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

一,介绍 本文实现带权图的最短路径算法.给定图中一个顶点,求解该顶点到图中所有其他顶点的最短路径 以及 最短路径的长度.在决定写这篇文章之前,在网上找了很多关于Dijkstra算法实现,但大部分是不带权的.不带权的Dijkstra算法要简单得多(可参考我的另一篇:无向图的最短路径算法JAVA实现):而对于带权的Dijkstra算法,最关键的是如何“更新邻接点的权值”.本文采用最小堆主辅助,以重新构造堆的方式实现更新邻接点权值. 对于图而言,存在有向图和无向图.本算法只需要修改一行代码,即可同时实

基于无向图且权重单一的最短路径Dijkstra算法——JAVA实现

做一个无向图的权重单一的最短路径算法. 模拟停车场最近车位的选择. 首先参考了博友JavaMan_chen的博文 http://blog.csdn.net/javaman_chen/article/details/8254309 但是这个算法是有问题的. 算法中,如果A点是当前点,是选取距离A点权重最小的那一点作为下一个路径点的. 这就带来了一个问题,即,距离A点的2个点如果权重相同,那就会随机选取其中一条. 于是,在数据量稍微大点的时候,就出错了. 在这里使用Dijkstra算法使用的是用OP

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

前言 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法.该算法被称为是“贪心算法”的成功典范.本文接下来将尝试以最通俗的语言来介绍这个伟大的算法,并赋予java实现代码. 一.知识准备: 1.表示图的数据结构 用于存储图的数据结构有多种,本算法中笔者使用的是邻接矩阵.  图的邻接矩阵存储方式是用两个数组来表示图.一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息. 设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为: 从上面可以看出,无向图的边

基于java最短路径算法公交查询系统的设计与实现

基于J2EE的公交查询系统的设计与实现 1引言 1.1 选题背景 20多年来,我国经济得到了持续.快速.稳定.健康地发展.经济的快速增长,带动了汽车工业的蓬勃发展,并使交通状况显著改善.据统计,中国公路通车总里程已达130余万公里,其中高速公路约1.5万公里.居民收入普遍提高,到2000年年底,人均GDP已超过800美元,沿海地区已达2000-3000美元.按国际发展惯例,当人均GDP超出1000美元,汽车消费市场就将进入快速增长期.我国城市人口约有2亿,略低于美国人口.东部沿海地区大部分居民已

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

一,问题描述 在英文单词表中,有一些单词非常相似,它们可以通过只变换一个字符而得到另一个单词.比如:hive-->five:wine-->line:line-->nine:nine-->mine..... 那么,就存在这样一个问题:给定一个单词作为起始单词(相当于图的源点),给定另一个单词作为终点,求从起点单词经过的最少变换(每次变换只会变换一个字符),变成终点单词. 这个问题,其实就是最短路径问题. 由于最短路径问题中,求解源点到终点的最短路径与求解源点到图中所有顶点的最短路径复

最短路径算法的命令式、函数式版本对比分析

C版本(来自 最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现(C/C++)) 1 /*************************************** 2 * About: 有向图的Dijkstra算法实现 3 * Author: Tanky Woo 4 * Blog: www.WuTianQi.com 5 ***************************************/ 6 7 #include <iostream> 8 using namespace

最短路径算法

最短路径-Dijkstra算法和Floyd算法 1.Dijkstra算法 1.1.定义概览Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等.注意该算法要求图中不存在负权边. 问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点

Algorithm --&gt; Dijkstra和Floyd最短路径算法

Dijkstra算法 一.最短路径的最优子结构性质 该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径.下面证明该性质的正确性. 假设P(i,j)={Vi....Vk..Vs...Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j).而 P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P'(k,s),那么 P'(i,

(最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法模板的整理与介绍

这一篇博客以一些OJ上的题目为载体.整理一下最短路径算法.会陆续的更新... 一.多源最短路算法--floyd算法 floyd算法主要用于求随意两点间的最短路径.也成最短最短路径问题. 核心代码: /** *floyd算法 */ void floyd() { int i, j, k; for (k = 1; k <= n; ++k) {//遍历全部的中间点 for (i = 1; i <= n; ++i) {//遍历全部的起点 for (j = 1; j <= n; ++j) {//遍历