Dijkstra算法(无负权值)

来源:互联网 发布:恋上我的帅和尚知乎 编辑:程序博客网 时间:2024/05/21 17:23

预备知识:边的松弛和顶点松弛,主要用于最短路径计算,所以默认有一个源节点(计算最短路径的起始节点),权重用距离表示

边的松弛:

private void relax(DirectedEdge e) {int v = e.from(), w = e.to();if (distTo[w] > distTo[v] + e.weight()) {distTo[w] = distTo[v] + e.weight();edgeTo[w] = e;}}

如果:源节点到w的距离>源节点到v的距离+边e的权重

那么更新w节点的信息:将边e指向w,源节点到w的权重是源节点到v的权重加边e的权重


顶点松弛:

private void relax(EdgeWeightedDigraph G, int v) {for (DirectedEdge e : G.adj(v)) {int w = e.to();if (distTo[w] > distTo[v] + e.weight()) {distTo[w] = distTo[v] + e.weight();edgeTo[w] = e;}}
}
给出一个节点v,遍历和v相连接的边e,w是e指向的另一个节点。和上面的边的松弛一样,如果源节点到w的距离大于源节点到v+边e的权重,那么更新w的信息,更新方法和上面一样。

可以说顶点松弛是边的松弛的“加强版”。顶点松弛相当于给出一个特定的顶点,对这个顶点的所有边进行松弛。


Dijkstra算法(不考虑负权值)

public DijkstraSP(EdgeWeightedDigraph G, int s) {edgeTo = new DirectedEdge[G.V()];distTo = new double[G.V()];pq = new IndexMinPQ<Double>(G.V());for (int v = 0; v < G.V(); v++)distTo[v] = Double.POSITIVE_INFINITY;distTo[s] = 0.0;pq.insert(s, 0.0);while (!pq.isEmpty())relax(G, pq.delMin());}
解释一下每个参数:

edgeTo:图每一个有向边

distTo: 图的每一条边的权重

pq:存放边的权重的索引优先队列,权重最低的优先级的元素先被访问,用来访问权重最小的节点

从程序很容易看出,这种不考虑负权值的算法不断放松权值最小的顶点,最后会生成一个最短路径树

以下图为例,放松0节点后获得2节点和4节点,两个节点都有指向7节点。接下来放松2节点,直接获得2->7这条路径。再放松4节点,获得4->7和4->5两条路径。由于0到4的权重加上4到7节点的权重要大于0-2-7这条路径的权重,所以舍弃4-7这一条路径,暂时保留4-5这条路径。在放松7节点的时候可以知道,0-4-5这条路径的权重小于(0-7的权重)+(7-5)的权重,所以舍去7-5这条路径。以此类推。

证明:假设v是顶点可达的,那么所有从v到w的路径都只会被放松一次。distTo[w]只会变小,不会变大,而distTo[v](v已经被加入了最短路径树)不会变。因此,当一个节点被加入树时,从源起点到达这个节点的路径一定是最短的。当所有可达节点都被加入树后,最短路径的最优性条件成立。

注意:这种算法和加权无向图的最小生成树Prim算法有类似的地方,Prim是每次把距离树最近的节点加入树中。

图摘自官网:

这是算法运行结束后的树


再附一张邻接表的结构:




0 0
原创粉丝点击