单源最短路径算法--Dijkstra算法和Bellman-Ford算法
Dijkstra算法
算法流程:
(a) 初始化:用起点v到该顶点w的直接边(弧)初始化最短路径,否则设为∞;
(b) 从未求得最短路径的终点中选择路径长度最小的终点u:即求得v到u的最短路径;
(c) 修改最短路径:计算u的邻接点的最短路径,若(v,…,u)+(u,w)<(v,…,w),则以(v,…,u,w)代替。
(d) 重复(b)-(c),直到求得v到其余所有顶点的最短路径。
特点:总是按照从小到大的顺序求得最短路径。
假设一共有N个节点,出发结点为s,需要一个一维数组prev[N]来记录前一个节点序号,一个一维数组dist[N]来记录从原点到当前节点最短路径(初始值为s到Vi的边的权值,没有则为+∞),一个二维数组weights[N][N]来记录各点之间边的权重,按以上流程更新prev[N]和dist[N]。
参考代码:
#include <iostream> #include <cstdlib> using namespace std; void Dijkstra(int n,int s,int *dist,int *prev,int w[][4]) { int maxint = 65535; bool *visit = new bool[n]; for (int i = 0; i < n; i++) { dist[i] = w[s][i]; visit[i] = false; if (dist[i] != maxint) { prev[i] = s; } } dist[s] = 0; visit[s] = true; for (int i = 0; i < n; i++) { int temp = maxint; int u = s; for (int j = 0; j < n; j++) { if ((!visit[j]) && (dist[j] < temp)) { u = j; temp = dist[j]; } } visit[u] = true; for (int j = 0; j < n; j++) { if (!visit[j]) { int newdist = dist[u] + w[u][j]; if (newdist < dist[j]) { dist[j] = newdist; prev[j] = u; } } } } delete []visit; } int main() { int n,v,u; int weight[4][4]={ 0,2,65535,4, 2,0,3,65535, 65535,3,0,2, 4,65535,2,0 }; int q = 0; int way[4]; int dist[4]; int prev[4]; int s = 1; int d = 3; Dijkstra(4, s, dist, prev, weight); cout<<"The least distance from "<<s<<" to "<<d<<" is "<<dist[d]<<endl; int w = d; while (w != s) { way[q++] = prev[w]; w = prev[w]; } cout<<"The path is "; for (int j = q-1; j >= 0; j--) { cout<<way[j]<<" ->"; } cout<<d<<endl; return 0; }
Bellman-Ford算法
Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题。对于给定的带权(有向或无向)图 G=(V,E),其源点为s,加权函数 w 是边集 E 的映射。对图G运行Bellman-Ford算法的结果是一个布尔值,表明图中是否存在着一个从源点s可达的负权回路。若不存在这样的回路,算法将给出从源点s到图G的任意顶点v的最短路径d[v]。
Bellman-Ford算法流程分为三个阶段:
(1)初始化:将除源点外的所有顶点的最短距离估计值 d[v] ←+∞, d[s] ←0;
(2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V中的每个顶点v的最短距离估计值逐步逼近其最短距离;(运行|v|-1次)
(3)检验负权回路:判断边集E中的每一条边的两个端点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解;否则算法返回true,并且从源点可达的顶点v的最短距离保存在 d[v]中。
算法描述如下:
Bellman-Ford(G,w,s) :boolean //图G ,边集 函数 w ,s为源点
1 for each vertex v ∈ V(G) do //初始化 1阶段
2 d[v] ←+∞
3 d[s] ←0; //1阶段结束
4 for i=1 to |v|-1 do //2阶段开始,双重循环。
5 for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。
6 If d[v]> d[u]+ w(u,v) then //松弛判断
7 d[v]=d[u]+w(u,v) //松弛操作 2阶段结束
8 for each edge(u,v) ∈E(G) do
9 If d[v]> d[u]+ w(u,v) then
10 Exit false
11 Exit true
适用条件和范围:
1.单源最短路径(从源点s到其它所有顶点v);
2.有向图&无向图(无向图可以看作(u,v),(v,u)同属于边集E的有向图);
3.边权可正可负(如有负权回路输出错误提示);
4.差分约束系统;
#include <stdio.h> #include <stdlib.h> /* Let INFINITY be an integer value not likely to be confused with a real weight, even a negative one. */ #define INFINITY ((1 << 14)-1) typedef struct { int source; int dest; int weight; } Edge; void BellmanFord(Edge edges[], int edgecount, int nodecount, int source) { int *distance =(int*) malloc(nodecount*sizeof(int)); int i, j; for (i=0; i < nodecount; ++i) distance[i] = INFINITY; distance[source] = 0; for (i=0; i < nodecount; ++i) { int nbChanges = 0; for (j=0; j < edgecount; ++j) { if (distance[edges[j].source] != INFINITY) { int new_distance = distance[edges[j].source] + edges[j].weight; if (new_distance < distance[edges[j].dest]) { distance[edges[j].dest] = new_distance; nbChanges++; } } } // if one iteration had no impact, further iterations will have no impact either if (nbChanges == 0) break; } for (i=0; i < edgecount; ++i) { if (distance[edges[i].dest] > distance[edges[i].source] + edges[i].weight) { puts("Negative edge weight cycles detected!"); free(distance); return; } } for (i=0; i < nodecount; ++i) { printf("The shortest distance between nodes %d and %d is %d\n", source, i, distance[i]); } free(distance); return; } int main(void) { /* This test case should produce the distances 2, 4, 7, -2, and 0. */ Edge edges[10] = {{0,1, 5}, {0,2, 8}, {0,3, -4}, {1,0, -2}, {2,1, -3}, {2,3, 9}, {3,1, 7}, {3,4, 2}, {4,0, 6}, {4,2, 7}}; BellmanFord(edges, 10, 5, 4); return 0; }