BellmanFord的队列优化
当我们使用BellmanFord算法时可以了解到,当我们第一次遍历时松弛的边是从源点可以直接到达的边,接着再从这些顶点可以直接到达的边进行松弛,以此类推。
所以基于以上思想我们可以去除BellmanFord算法中的无效循环。
首先我们可以选择使用连个数组first和next,first用于记录每条边的第一条边(最后一次输入的边),next用于记录每条边的上一条边,如果没有上一条边则next为-1。再使用que数组(队列)存放已经松弛过的顶点,book数组用来记录顶点是否已经入队。首先,我们需要先把源点入队,且记录下源点已经入队。我们默认从源点到其他所有顶点的距离为999(无穷大)。我们从源点开始找出其第一条出边(1->5 10)接下来的判断就和BellmanFord中一样,松弛该边成功,顶点5入队。接着找出源点开始的第二条边(1->2 2),松弛成功,将2号顶点入队。此时我们发现从源点开始可以直接到达的顶点都没有了,然后我们把源点出队,从队列中的第二个顶点(5)开始向下搜索其可直接到达的边进行松弛,以此类推,直到队列中没有顶点为止。
int[] u = new int[7];
int[] v = new int[7];
int[] w = new int[7];
int[] first = new int[5];//顶点个数
int[] next = new int[7];//边的条数
int[] dis = new int[5];//原点到各个点之间的距离
int[] book = new int[5];
int head = 0;
int tail = 1;
int[] que = new int[20];
private void initArrs() {
for (int i = 1; i <= 4; i++) {
dis[i] = 999;
}
for (int i = 0; i <= 4; i++) {
first[i] = -1;
}
u[0] = 0;
v[0] = 1;
w[0] = 2;
u[1] = 0;
v[1] = 4;
w[1] = 10;
u[2] = 1;
v[2] = 2;
w[2] = 3;
u[3] = 1;
v[3] = 4;
w[3] = 7;
u[4] = 2;
v[4] = 3;
w[4] = 4;
u[5] = 3;
v[5] = 4;
w[5] = 5;
u[6] = 4;
v[6] = 3;
w[6] = 6;
for (int i = 0; i <= 6; i++) {
next[i] = first[u[i]];
first[u[i]] = i;
}
}
@Test
public void testBellmanFordBetter() {
initArrs();
book[0] = 1;
que[0] = 0;
while (head < tail) {
int k = first[que[head]];
while (k != -1) {
if (dis[v[k]] > dis[u[k]] + w[k]) {
dis[v[k]] = dis[u[k]] + w[k];
if (book[v[k]] == 0) {
que[tail++] = v[k];
book[v[k]] = 1;
}
}
k = next[k];
}
book[que[head]] = 0;
head++;
}
for (int i = 0; i <= 4; i++) {
System.out.println(dis[i]);
}
}