最短路径算法

Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法。该算法被称为是“贪心算法”的成功典范。

1、表示图的数据结构

邻接列表

邻接列表:在邻接列表实现中,每一个顶点会存储一个从它这里开始的边的列表。比如,如果顶点A 有一条边到B、C和D,那么A的列表中会有3条边

邻接列表只描述了指向外部的边。A 有一条边到B,但是B没有边到A,所以 A没有出现在B的邻接列表中。查找两个顶点之间的边或者权重会比较费时,因为遍历邻接列表直到找到为止。

邻接矩阵

邻接矩阵:在邻接矩阵实现中,由行和列都表示顶点,由两个顶点所决定的矩阵对应元素表示这里两个顶点是否相连、如果相连这个值表示的是相连边的权重。例如,如果从顶点A到顶点B有一条权重为 5.6 的边,那么矩阵中第A行第B列的位置的元素值应该是5.6:

、、

图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。

设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

  

从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

从这个矩阵中,很容易知道图中的信息。

(1)要判断任意两顶点是否有边无边就很容易了;

(2)要知道某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;

(3)求顶点vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点;

而有向图讲究入度和出度,顶点vi的入度为1,正好是第i列各数之和。顶点vi的出度为2,即第i行的各数之和。

算法实现思路

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

源点的最短路径距离为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}

完整代码实现

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);}}
}

图理解

参考链接

https://blog.csdn.net/yu121380/article/details/79810233

对于求最短路径的问题一般都会给出一幅图,或者边与边的关系。如上图。

假设我们起点是A,我们要求到F的最短距离,我们会怎么做? 
首先,因为A是起点,所以我们把对于每个点都有个参数,相对于A的距离,默认除了A到A为0,其他都是无穷大。

从起点A开始,我们更新与A相连通的点到A的距离,并把A点标记。如图: 

我们遍历一次所有点与A的距离,找到最小的,这里是点B。 
以它为起点,把它周围未被标记的点拿来做比较,显然,像F这种没有与A练过的点,当前距离就会变成min(dis[B]+maze[B][F],INF),就是B到起点的距离加上BF之间的距离。而像c这种与A直接相连的点,当前距离就会变成min(dis[B]+maze[B][C],dis[c]),所以按照我们每次只需要比较当前点到当前状态起点的和与当前点到起点的距离就可以了。 

然后我们遍历发现,当前未被标记且到起点距离最小点的点是C点。我们更新连接了C的所有点。同样利用上面的min公式。

同理,更新D点的邻点。

再把更新E点的邻点。

最后再更新F点。发现F点周围所有点都被标记了,不做更新。 
再遍历,发现图中所有点都被遍历了,算法结束。 
这个时候,我们已经求出了所有点到起点的最小距离。 
可以直接输出dis[F]求得A到F的最短路径。

注意: 
上面的图重点是看每次变化找的起点和与出发点路径的变化。 
当前起点是当前未被标记且到出发点距离最近的点。 
更新的点都是与该起点直接相连并未被标记的点。

因为并没有无穷大这个数,所以我们把INF设为一个我们计算不能到达的数,这里的数接近int的上限,可以近似看作正无穷。
————————————————

#include"iostream"
#include"cstring"
#include"cstdio"using namespace std;
#define INF 0x7f7f7f7fconst int N = 105; //点的个数上限int maze[N][N];
int dis[N];
bool vis[N];//点的个数和边的条数
int n,m;void init()
{memset(maze,INF,sizeof(maze));memset(dis,INF,sizeof(dis));memset(vis,false,sizeof(vis));
}void dijkstra(int st)
{dis[st]=0;for(int i=1; i<=n; i++){//找到和起点距离最短的点int minx=INF;int minmark;for(int j=1; j<=n; j++){if(vis[j]==false&&dis[j]<=minx){minx=dis[j];minmark=j;}}//并标记vis[minmark]=true;//更新所有和它连接的点的距离for(int j=1; j<=n; j++){if(vis[j]==false&&dis[j]>dis[minmark]+maze[minmark][j])dis[j]=dis[minmark]+maze[minmark][j];}}
}int main()
{while(scanf("%d %d",&n,&m)!=EOF){if(n==0&&m==0) break;//每组数据都要初始化init();for(int i=1; i<=m; i++){int x,y,len;scanf("%d %d %d",&x,&y,&len);if(x!=y&&maze[x][y]>len){maze[y][x]=len;maze[x][y]=len;}}//以1为起点跑一次dijdijkstra(1);//输出到n的距离printf("%d\n",dis[n]);}
}

最短路径算法--无向图相关推荐

  1. (最短路径算法整理)dijkstra、floyd、bellman-ford、spfa算法

    一.floyd 1.介绍 floyd算法只有五行代码,代码简单,三个for循环就可以解决问题,所以它的时间复杂度为O(n^3),可以求多源最短路问题. 2.思想: Floyd算法的基本思想如下:从任意 ...

  2. 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)

    带权图分为有向和无向,无向图的最短路径又叫做最小生成树,有prime算法和kruskal算法:有向图的最短路径算法有dijkstra算法和floyd算法. 生成树的概念:联通图G的一个子图如果是一棵包 ...

  3. 几大最短路径算法比较

    用于解决最短路径问题的算法被称做"最短路径算法",有时被简称作"路径算法".最常用的路径算法有: Dijkstra算法.A*算法.SPFA算法.Bellman- ...

  4. 最小生成树(prime算法、kruskal算法) 和 最短路径算法(floyd、dijkstra)

    带权图分为有向和无向,无向图的最短路径又叫做最小生成树,有prime算法和kruskal算法:有向图的最短路径算法有dijkstra算法和floyd算法. 生成树的概念:联通图G的一个子图如果是一棵包 ...

  5. 沃舍尔算法_[数据结构拾遗]图的最短路径算法

    前言 本专题旨在快速了解常见的数据结构和算法. 在需要使用到相应算法时,能够帮助你回忆出常用的实现方案并且知晓其优缺点和适用环境.并不涉及十分具体的实现细节描述. 图的最短路径算法 最短路径问题是图论 ...

  6. 图的最小生成树和最短路径算法思路总结(Prim,Kruskal,Dijkstra,Floyd)

    带权无向图->最小生成树算法->Prim算法: 思路: 首先,我们先设置两个集合,U_{}:一个用来放最小生成树的顶点,T_{}:一个用来放最小生成树的边.选取最开始的点V_0,将V_0放 ...

  7. Python小白的数学建模课-17.条件最短路径算法

    条件最短路径问题,指带有约束条件.限制条件的最短路径问题.例如: 顶点约束,包括必经点或禁止点的限制: 边的约束,包括必经路段.禁行路段和单向路段:无权路径长度的限制,如要求经过几步或不超过几步到达终 ...

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

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

  9. aes算法c语言实现_C语言实现常用数据结构:Dijkstra最短路径算法(第18篇)

    「今天是学习C语言第 161 天」 纸上学来终觉浅,绝知此事要躬行.-- 陆游「冬夜读书示子聿」#题外话算法学习重点是学习如何编程使用它. # Dijkstra算法 Dijkstra算法,中文译名迪杰 ...

最新文章

  1. 再学Android之多线程
  2. innodb表空间结构
  3. python数据插值_【Python】【数据分析】缺失值处理——插值
  4. android 解压版sdk安装的问题
  5. 成功解决matplotlib绘图中描述性字体特殊符号(比如数值的右上角标和右下角标表示)
  6. awk 处理json
  7. (亲测)vue-cli项目添加骨架屏多种方式,自动生成骨架屏
  8. 用python数据分析excel多地天气_Python实现天气查询功能(外加Excel技巧)
  9. 计算机组成原理页表长度,计算机组成原理(2)-虚拟存储器
  10. linux中脚本后台执行的方法
  11. 机器学习入门——机器学习基础概念
  12. 在命令行英雄的浏览器大战中,JavaScript令人惊讶地崛起
  13. 基于vhdl的分频器设计
  14. 编程之道 The Tao Of Programming
  15. python画图的函数_python画图函数
  16. java开发工具排名_排名前16的Java工具类
  17. linpack实验:MPI代码调优
  18. VirtualBox - 让分辨率自适应窗口大小
  19. java对台湾同胞身份证号码验证
  20. kafka启动异常InconsistentClusterIdException

热门文章

  1. 深入浅出 Fast DDS网络协议(入门篇)
  2. c语言菜单选择如何用字符形式,第1章 概论-C语言
  3. php怎么取随机3位数字,php 从指定数字中获取随机组合的方法
  4. 教你一招轻松搞定广告配音
  5. msb307命令setlocal_记录VS2019生成项目提示MSB307命令dotnet paket restore已退出,代码为1....
  6. 撕裂者cpu三代文件服务器,AMD 两年 CPU 线路图曝出,10月发第三代线程撕裂者
  7. 极客战记计算机科学2村庄守卫,「网易官方」极客战记(codecombat)攻略-森林-村庄守卫-village-warder...
  8. zookeeper中展示所有节点_记录一次zookeeper集群其中一节点在hbase web页面中显示Connection rese...
  9. 【图片新闻】80万美元一枚的LRLAP炮弹让朱姆沃尔特级驱逐舰“心力交瘁”
  10. xshell与xftp安装和使用