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

如需转载,请注明出处 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. 手机淘宝中的那些Web技术-使用了类似PhoneGap的实现

    Native APP与Web APP的技术融合已经逐渐成为一种趋势,使用标准的Web技术来开发应用中的某些功能,不仅可以降低开发成本,同时还可以方便的进行功能迭代更新.但是如何保证Web APP的流畅 ...

  2. C# 汉字转拼音 将中文转换成拼音

    //建立一个公共类,用于转换汉字 public class ChnToPh { //定义拼音区编码数组 private static int[] getValue = new int[] { -,-, ...

  3. 设计模式 -- 迭代器模式(Iterator)

    --------------------------------------------------------------------- 1.场景问题 考虑这样一个问题: 9个学生对象分别通过数组存 ...

  4. PS切图篇(一)---界面设置

    #工作区设置 四大主要面板:信息 字符  图层 历史记录 打开必要属性: 选择工具设置 选择图层的方式:ctrl+鼠标左击想选择的图层

  5. 解决 Eclipse build workspace validation javascript 慢的问题

    参考: http://blog.csdn.net/zhangzikui/article/details/24805935 http://www.cnblogs.com/wql025/p/4978351 ...

  6. Python单元测试PyUnit框架轻度整改

    原理 参考:单元测试原理 背景 年后有段时间没写代码了,所以趁着周末找了个python单元测试玩下,测试自己的Android应用.发现PyUnit虽然在单个脚本文件中添加多个测试用例,比如官网提供的方 ...

  7. iOS ReactiveCocoa(RAC)学习详解

    概述: ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的一个框架,有时间,自己也了解学习了一下这个框架的一些基本的应用,其实你要学的话网上是有很多很多的博 ...

  8. Swift 网络请求数据与解析

    一: Swift 网络数据请求与处理最常用第三方 又有时间出来装天才了,还是在学swift,从中又发现一些问题,这两天上网找博客看问题弄的真的心都累.博客一篇写出来,好多就直接照抄,就没有实质性的把问 ...

  9. C语言 二维数组复制、清零及打印显示

    #include <stdlib.h> #include <stdio.h> #include <string.h> //二维整型数组打印显示 ],int row, ...

  10. 面向UI编程:ui.js 1.1 使用观察者模式完成组件之间数据流转,彻底分离组件之间的耦合,完成组件的高内聚

    开头想明确一些概念,因为有些概念不明确会导致很多问题,比如你写这个框架为什么不去解决啥啥啥的问题,哎,心累. 什么是框架? 百度的解释:框架(Framework)是整个或部分系统的可重用设计,表现为一 ...