欢迎探讨,如有错误敬请指正

如需转载,请注明出处 http://www.cnblogs.com/nullzx/


1. 算法的原理

以源点开始,以源点相连的顶点作为向外延伸的顶点,在所有这些向外延伸的顶点中选择距源点最近的顶点继续向四周延伸(某个顶点被选作继续延伸的顶点,则源点到它的最短距离就已经确定,我们也不再将其视为向外延伸的顶点了),如果在继续延伸的过程中遇到了之前已延伸到的顶点,且当前这次延伸过程使其离源点更近,我们就修正这个距离,直到所有的顶点都被视为继续延伸的顶点,此时我们就得到了源点到其它各个顶点的距离。

2. 一个具体的例子

在下面的例子中,模拟了dijkstra算法求解顶点3到其它各个顶点的最短距离。

黑色的顶点表示没有被延伸到的顶点,此时源点到它的距离为无穷。红色顶点表示已被延伸到的顶点,红色顶点旁的数字表示源点到它的距离。绿色顶点表示源点到该顶点的最短距离已确定。如果源点到某个顶点的距离被修正,我们将用黄色的方框将其标注。

distTo数组表示源点(下图中源点为顶点3)到各个顶点的距离,其中绿色的表示最短距离,红色表示这个距离是否是最短距离还未确定。

edgeTo数组表示源点3(下图中源点为顶点3)到各个顶点的路径,其中绿色的表示最短距离路径确定,红色表示这个距离是否是最短距离还未确定。edgeTo[i]表示经过顶点i的上一个顶点。

初始时,将源点看做向外延伸的顶点,它到自身的距离为0.0。每次向外延伸的过程中我们都会从红色的顶点中选择距离最近的顶点继续向外延伸,然后把该顶点标注成绿色。

下面是源点到各个节点的最短路径

最后我们从distTo和edgeTo数组就可以找到最短的距离和路径。

假设我们想得到源点,即顶点3,到顶点1的距离和路径。

distTo[1]的值为5.0,说明最短距离为5.0。

edgeTo[1]说明到顶点1的上一个顶点为顶点10,edgeTo[10]说明到顶点10的上一个顶点为4,edgeTo[4]说明到顶点4的上一个顶点为顶点3。也就是路径为3->4->10->1

3. 算法的证明

要证明算法的正确性,我们实际上需要证明:当从延伸顶点中选择离源点最近的顶点继续向外延伸时,源点到它的最短距离就已经确定了。

假设当前延伸顶点中最短的距离为t,同时存在一条路径从已访问到的顶点k经过某些未知顶点x到达t的距离更小。

已知distTo[s->t] <= distTo[s->k],

如果distTo[k->x]以及distTo[x->]都大于零, 那么

distTo[s->t] < distTo[s-k] + distTo[k->x] + distTo[x->]

上述不等式显然不会成立,所以算法的正确性得以证明。同时我们得出了Dijistra算法的适用情况:边的权值都为非负值

显然,如果是求给定两点st的最短距离,我们只需要在延伸的过程中将t作为继续向四周延伸的顶点时停止算法即可

4. 算法的实现

测试数据

8

15

4 5    0.35

5 4    0.35

4 7    0.37

5 7    0.28

7 5    0.28

5 1    0.32

0 4    0.38

0 2    0.26

7 3    0.39

1 3    0.29

2 7    0.34

6 2    0.40

3 6    0.52

6 0    0.58

6 4    0.93

输出结果

0 : [4 , 0.38] [2 , 0.26]

1 : [3 , 0.29]

2 : [7 , 0.34]

3 : [6 , 0.52]

4 : [5 , 0.35] [7 , 0.37]

5 : [4 , 0.35] [7 , 0.28] [1 , 0.32]

6 : [2 , 0.40] [0 , 0.58] [4 , 0.93]

7 : [5 , 0.28] [3 , 0.39]

1.51

0 2 0.26

2 7 0.34

7 3 0.39

3 6 0.52

源代码

package datastruct;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;

import datastruct.Graph.Edge;

public class Dijkstra {
	private double[] distTo;
	private Edge[] edgeTo;
	private PriorityQueue<DistTo> pq;
	private Graph g;
	private int s;

	//源点到顶点V的距离,
	public static class DistTo implements Comparable<DistTo>{
		public int idx;    //顶点的编号
		public double dist;//源点到顶点idx的短距离

		public DistTo(int v, double totalDist){
			this.idx = v;
			this.dist = totalDist;
		}

		@Override
		public int compareTo(DistTo that) {
			if(this.dist > that.dist){
				return 1;
			}else
			if(this.dist < that.dist){
				return -1;
			}else{
				return 0;
			}
		}
	}

	public Dijkstra(Graph g, int s){
		this.g = g;
		this.s = s;
		shortPath();
	}

	private void shortPath(){
		edgeTo = new Edge[g.V()];

		distTo = new double[g.V()];
		Arrays.fill(distTo, Double.POSITIVE_INFINITY);
		distTo[s] = 0.0;

		//如果到源点到顶点v的距离被多次修正,那么优先队列中就可能存在到顶点v的多个距离
		//所以以边的个数作为优先队列的最大长度
		pq = new PriorityQueue<DistTo>(g.E());

		pq.offer(new DistTo(s, 0.0));
		while(!pq.isEmpty()){
			DistTo v = pq.poll();//每次从优先队列中找到最近的延伸顶点
			for(Edge e : g.adjEdge(v.idx)){//从与idx顶点的出边继续向下延伸
				int to = e.to();
				if(v.dist + e.weight() < distTo[to]){
					edgeTo[to] = e;//修正路径
					distTo[to] = v.dist + e.weight();//修正距离
					pq.offer(new DistTo(to, distTo[to]));//修正后入列
				}
			}
		}
	}

	public double shortDistTo(int v){
		return distTo[v];
	}

	public List<Edge> shortPathTo(int v){
		LinkedList<Edge> stack = new LinkedList<Edge>();
		int to = v;
		if(distTo[to] == Double.POSITIVE_INFINITY){
			return stack;
		}

		while(edgeTo[to] != null){
			stack.push(edgeTo[to]);
			to = edgeTo[to].from();
		}
		return stack;
	}

	public static void main(String[] args) throws FileNotFoundException{
		File path = new File(System.getProperty("user.dir")).getParentFile();
		File f = new File(path, "algs4-data/tinyEWD.txt");
		FileReader fr = new FileReader(f);
		Graph g = new Graph(fr, true, true);
		System.out.println(g);

		Dijkstra dijkstra = new Dijkstra(g, 0);
		System.out.printf("%.2f\n", dijkstra.shortDistTo(6));
		List<Edge> shortPath = dijkstra.shortPathTo(6);
		for(Edge e : shortPath){
			System.out.println(e);
		}
	}
}

有关Graph类的实现可参照我技术博客的另一篇文章:

Kosaraju算法解析: 求解图的强连通分量

4. 参考内容

[1]. 算法(第4版)Robert Sedgewick 人民邮电出版社

[2]  Dijkstra算法(单源最短路径)

单源最短路径问题之dijkstra算法的更多相关文章

  1. 单源最短路径问题2 (Dijkstra算法)

    用邻接矩阵 /* 单源最短路径问题2 (Dijkstra算法) 样例: 5 7 0 1 3 0 3 7 1 2 4 1 3 2 2 3 5 2 4 6 3 4 4 输出: [0, 3, 7, 5, 9 ...

  2. 图论(四)------非负权有向图的单源最短路径问题,Dijkstra算法

    Dijkstra算法解决了有向图G=(V,E)上带权的单源最短路径问题,但要求所有边的权值非负. Dijkstra算法是贪婪算法的一个很好的例子.设置一顶点集合S,从源点s到集合中的顶点的最终最短路径 ...

  3. 单源最短路径—Bellman-Ford和Dijkstra算法

    Bellman-Ford算法:通过对边进行松弛操作来渐近地降低从源结点s到每个结点v的最短路径的估计值v.d,直到该估计值与实际的最短路径权重相同时为止.该算法主要是基于下面的定理: 设G=(V,E) ...

  4. 单源最短路径问题1 (Bellman-Ford算法)

    /*单源最短路径问题1 (Bellman-Ford算法)样例: 5 7 0 1 3 0 3 7 1 2 4 1 3 2 2 3 5 2 4 6 3 4 4 输出: [0, 3, 7, 5, 9] */ ...

  5. 图->最短路径->单源最短路径(迪杰斯特拉算法Dijkstra)

    文字描述 引言:如下图一个交通系统,从A城到B城,有些旅客可能关心途中中转次数最少的路线,有些旅客更关心的是节省交通费用,而对于司机,里程和速度则是更感兴趣的信息.上面这些问题,都可以转化为求图中,两 ...

  6. 单源最短路径-迪杰斯特拉算法(Dijkstra's algorithm)

    Dijkstra's algorithm 迪杰斯特拉算法是目前已知的解决单源最短路径问题的最快算法. 单源(single source)最短路径,就是从一个源点出发,考察它到任意顶点所经过的边的权重之 ...

  7. 单源最短路径 Bellman_ford 和 dijkstra

    首先两个算法都是常用于 求单源最短路径 关键部分就在于松弛操作 实际上就是dp的感觉 if (dist[e.to] > dist[v] + e.cost) { dist[e.to] = dist ...

  8. PAT甲级——1111 Online Map (单源最短路经的Dijkstra算法、priority_queue的使用)

    本文章同步发布在CSDN:https://blog.csdn.net/weixin_44385565/article/details/90041078   1111 Online Map (30 分) ...

  9. 单源最短路:Dijkstra算法 及 关于负权的讨论

    描述: 对于图(有向无向都适用),求某一点到其他任一点的最短路径(不能有负权边). 操作: 1. 初始化: 一个节点大小的数组dist[n] 源点的距离初始化为0,与源点直接相连的初始化为其权重,其他 ...

随机推荐

  1. thinkphp 配置项总结

    'URL_PATHINFO_DEPR'=>'-',//修改URL的分隔符 'TMPL_L_DELIM'=>'<{', //修改左定界符 'TMPL_R_DELIM'=>'}&g ...

  2. Flex中操作XML的E4X方法

    用于处理 XML 的 E4X 方法 Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本   ECMAScript for XML 规范定义了一组用于使用 XML 数据的类 ...

  3. servlet中的8个Listener

    servlet中的8个Listener: Xml: <?xml version="1.0" encoding="UTF-8"?> <web-a ...

  4. ADO.NET高级应用

    ADO.NET事务处理(4个步骤) 1.调用SqlConnection对象的BeginTransaction()方法,创建一个SqlTransaction对象,标志事务开始. 2.将创建的SqlTra ...

  5. java实现简单的单点登录_转

    摘要:单点登录(SSO)的技术被越来越广泛地运用到各个领域的软件系统当中.本文从业务的角度分析了单点登录的需求和应用领域:从技术本身的角度分析了单点登录技术的内部机制和实现手段,并且给出Web-SSO ...

  6. cmake工具链

    命令project() enable_language()try_compile() 变量CMAKE_<LANG>_COMPILERCMAKE_<LANG>_COMPILER_ ...

  7. std::vector 源代码

    vector身为一个动态数组,每次以空间不够的时候会以2倍的倍数增加,而且每次扩充的时候分为3部,分配内存,拷贝数据,释放内存 vector内部有两个成员变量,begin,finish ,endcat ...

  8. Glusterfs下读写请求的处理流程

    Glusterfs基于内核的fuse模块,fuse模块除了创建fuse文件系统外,还提供了一个字符设备(/dev/fuse),通过这个字符设备,Glusterfs可以读取请求,并发送响应,并且可以发送 ...

  9. python 模块加载错误总结

    在运行yum时提示如下错误 There was a problem importing one of the Python modules required to run yum. The error ...

  10. IP数据报格式 及路由转发算法

    ip数据报分首部和数据两部分组成: 首部分为固定部分和可变部分 版本--占 4 位,指 IP 协议的版本 目前的 IP 协议版本号为 4 (即 IPv4) 首部长度--占 4 位,可表示的最大数值 是 ...