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

如需转载,请注明出处 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. 用mfix模拟流化床时压力边界条件和迭代步长需要注意的问题

    没想到今天模拟一个冷态流化床都出现这么多问题.需要通入三种气体组成的混合物,这时入口边界的压力BC_P_g不能为零,否则会报错,但是,需要注意的是,收敛效果对这个压力边界非常敏感,我随意给了个30,结 ...

  2. HDU-1256-画8

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1256 这题做的我好苦,开始题目理解错了,一直都不对,仔细看题,别急,慢慢来,别紧张, 难度到不大,但题 ...

  3. 在ubuntu下增加root用户并登录

    1.打开终端. 2.输入sudo gedit /usr/share/lightdm/lightdm.conf.d/50-ubuntu.conf 3.在弹出的编辑框里输入:greeter-show-ma ...

  4. jenkins用户权限配置错误,导致登录时提示:没有Overall/read权限

    jenkins用户权限配置错误,导致登录时提示:没有Overall/read权限 由于初次接触jenkins,于是在搭建好jenkins以后,想要对用户进行管理,于是乎开始在系统管理->conf ...

  5. Linux安装配置VPN服务器

    一.实验简介 VPN ,中文翻译为虚拟专有网络,英文全称是 Virtual Private Network .现在 VPN 被普遍定义为通过 一个公用互联网络建立一个临时的.安全的连接,是一条穿过混乱 ...

  6. Apache的.htaccess到Nginx的转换

    今天项目要求从Apache转到Nginx,遇到了要将原来的rewrite规则移过来的问题,找了半天资源,居然有一个转换工具,地址如下: http://www.anilcetin.com/convert ...

  7. FMS带宽的需求计算法

    在开始一个使用 FLASH MEDIA SERVER的项目开始之前,最好能够对你项目使用FLASH MEDIA SERVER 3的带宽需求进行计算.这样对你的项目最终的实现效果,会有一个稳定的结果:去 ...

  8. js冒泡排序及计算其运行时间

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  9. Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容

    Bootstrap入门(十六)组件10:well和具有响应式特性的嵌入内容 well组件可以为内容增添一种切入效果. 具有响应式特性的嵌入内容可以根据被嵌入内容的外部容器的宽度,自动创建一个固定的比例 ...

  10. java cooki的使用

    session: 当新客户端发现一个HTTP请求时服务端会创建一个session.并分配一个sessionID作为服务端来客户端的识别,session对象会 保存在服务端.此时session对象处天N ...