您的位置:首页 > 其它

每对顶点的最短路径 : Johnson 算法

2013-08-30 16:05 465 查看
import org.apache.commons.lang.builder.ToStringBuilder;

/**
* 稀疏图上的Johnson算法 结合 BellmanFord 和 Dijkstra 算法
*
*/
public class Johnson {
static final int M = 10000; // unreachable

/**
* <pre>
* @param edges : G(V,E)
* @param dist : 计算的最短路径结果
* @param p : 记录前序节点
* @param s : 源点
* @return
* </pre>
*/
static boolean bellmanFord(int[][] edges, int[] dist, int[] p, int s) {
int n = edges.length;
if (dist == null) {
dist = new int
;
}
if (p == null) {
p = new int
;
}
// init
for (int i = 0; i < n; i++) {
dist[i] = M;
p[i] = -1;
}
dist[s] = 0;
p[s] = -1;

// 最短路径中最多含有n-1条边
for (int cnt = 1; cnt < n; cnt++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (edges[i][j] < M && dist[j] > (dist[i] + edges[i][j])) {
dist[j] = dist[i] + edges[i][j];
p[j] = i;
}
}
}
}

// 判断是否存在负权环
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (edges[i][j] < M) {
if (dist[j] > dist[i] + edges[i][j]) // 存在
return false;
}
}
}
return true;
}

/**
* <pre>
* @param edges : G(V,E)
* @param dist : 计算的最短路径结果
* @param p : 记录前序节点
* @param s : 源点
* </pre>
*/
static void dijkstra(int[][] edges, int[] dist, int[] p, int s) {
int n = edges.length;
if (dist == null) {
dist = new int
;
}
if (p == null) {
p = new int
;
}

// init
boolean[] visited = new boolean
;
for (int i = 0; i < n; i++) {
dist[i] = Integer.MAX_VALUE;
p[i] = -1;
}
// 源点的值置为0
dist[s] = 0;
p[s] = -1;

// n个点,n次循环
int cnt = n;
while (cnt > 0) {
cnt--;
int idx = -1;
int min = Integer.MAX_VALUE;
// 选取dist中值最小的一个点, 用最小堆更好
for (int i = 0; i < n; i++) {
if (!visited[i] && min > dist[i]) {
min = dist[i];
idx = i;
}
}
// union
visited[idx] = true;
// 计算最短路径
for (int j = 0; j < n; j++) {
if (dist[j] > (dist[idx] + edges[idx][j])) {
dist[j] = dist[idx] + edges[idx][j];
p[j] = idx;
}
}
}

}

/**
* 根据索引,返回对应的节点名称
*
* @param i
* @return
*/
static char node(int i) {
return (char) ('A' + i);
}

/**
* 打印从u->v的最短路径
*
* <pre>
* @param p : 记录前序节点
* @param u : 起始节点
* @param v : 终止节点
* @param d : 最短路径值
* </pre>
*/
static void printPath(int[] p, int u, int v, int d) {

if (d == M) {
System.out.printf("%c -> %c (∞) : \n", node(u), node(v));
return;
} else {
System.out.printf("%c -> %c (%d) : ", node(u), node(v), d);
StringBuilder sb = new StringBuilder();
sb.append(node(v));
while (p[v] != -1) {
sb.insert(0, node(p[v]) + " - ");
v = p[v];
}
System.out.println(sb.toString());
}
}

/**
* <pre>
* @param _edges
* 1.通过增加一个s节点,并增加s->V的路径(edge[s][v] = 0; edge[v][s] = M;),并建立有向图G'
* 2.对于G', 用Bellman-Ford 算法判断是否存在负权环
* 3.如果存在,退出,不存在,计算最短路径(int[] h)和前序节点
* 4.Reweighting, W(u,v)' = W(u, v) + h(u) - h(v);
* 5.通过Dijkstra算法,计算图G'以每个节点为源点的最短路径
* 6.对计算得到的最短路径值进行恢复dist[u][v] = d[v] - h[u] + h[v];
* </pre>
*/
static void johnson(int[][] _edges) {
int _n = _edges.length;

//增加源点s, 构建图G'
int s = _n;
int n = _n + 1;
int[][] edges = new int

;
int[][] dist = new int[_n][_n];
for (int i = 0; i < _n; i++) {
for (int j = 0; j < _n; j++) {
edges[i][j] = _edges[i][j];
dist[i][j] = M;
}
edges[i][_n] = M;
}

//判断是否存在负权环,并且计算h
int[] h = new int
;
boolean noNegativeCycle = bellmanFord(edges, h, null, s);
if (!noNegativeCycle) {
System.out.println("G(V,E) exist negative cycle!");
return;
}

//Reweighting
for (int i = 0; i < _n; i++) {
for (int j = 0; j < _n; j++) {
if (i != j && _edges[i][j] < M)
edges[i][j] = _edges[i][j] + h[i] - h[j];
}
}
for (int i = 0; i < _n; i++) {
for (int j = 0; j < _n; j++) {
_edges[i][j] = edges[i][j];
}
}

// 通过Dijkstra算法,计算每个顶点为源点的最短路径
for (int i = 0; i < _n; i++) {
int[] d = new int[_n];
int[] p = new int[_n];
dijkstra(_edges, d, p, i);
for (int j = 0; j < _n; j++) {
//数据恢复,得到最短路径
dist[i][j] = d[j] - h[i] + h[j];
//print
printPath(p, i, j, dist[i][j]);
}
System.out.println();
}
}

public static void main(String[] args) {
//		int[][] edges = {
//				{ 0, 3, 8, M, -4 },
//				{ M, 0, M, 1, 7 },
//				{ M, 4, 0, M, M },
//				{ 2, M, -5, 0, M },
//				{ M, M, M, 6, 0 }
//			};

int[][] edges = {
{0,	M,	M,		M, M, M, 9},
{20, 	0, 	10,	30, M, M, M},
{M, 	M, 0, 	M, 5, M, 18},
{M, 	M, M, 	0, M, M, M},
{M, 	M, M, 	12, 0, M, 15},
{M, 	M, M, 	M, 8, 0, 10},
{M, 	M, 18, 	M, M, M, 0}
};

johnson(edges);

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息