1. 程式人生 > >SPFA——基於Bellman-Ford的佇列優化

SPFA——基於Bellman-Ford的佇列優化

Bellman-Ford演算法在每一次實施鬆弛操作時,就會有一些頂點已經求得最短路徑,此後這些頂點的最短路徑的估計值就會一直保持不變,不再受後續鬆弛操作的影響,但是每次還要判斷是否需要鬆弛,這裡浪費了大量的時間.

SPFA(Shortest Path Faster Algorithm)是基於Bellman-Ford演算法的改進,每次進隊最短路徑估計值發生變化了的頂點的所有出邊執行鬆弛操作,藉助一個佇列.

思想:每次選取隊首頂點u,對頂點u的所有出邊進行鬆弛操作,例如有一條u->v的邊,如果通過u->v的這條邊使得源點到頂點v的最短路徑變得更短,也即(dis[u] + e[u][v] < dis[v]),並且頂點v不在當前的佇列(並用藉助一個數組標記頂點是否已經在佇列之中),則將頂點v放置隊尾. 對頂點u的所有出邊鬆弛完畢之後,就將頂點u出隊,接下來不斷從佇列中取出新的隊首頂點反覆上面操作直到佇列為空結束.

用佇列優化的Bellman-Ford演算法的關鍵之處:只有那些在前一遍鬆弛中改變了最短路徑估計值的點,才可能引起它們鄰接點最短路徑估計值發生改變. 因此用一個佇列儲存被成成功鬆弛的頂點,之後只對佇列中的點進行處理,這就降低了演算法的時間複雜度.

Q:SPFA如何判斷一個圖是否有負環?

如果某個點進入佇列次數超過n次,那麼這個圖肯定存在負環.


Code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define INF 999999
int book[10];              /// 初始化為頂點都不在佇列
int que[100];              /// 鬆弛成功並且頂點不在佇列則併入佇列

int main(int argc, char const *argv[])
{
	int i, j, n, m;
	int q1, q2, q3;
	int dis[10], e[10][10];
	int head, tail;

	scanf("%d %d", &n, &m);

	for(i = 1; i <= n; ++i)
	{
		for(j = 1; j <= n; ++j)
		{
			if(i == j)
			{
				e[i][j] = 0;
			}
			else
			{
				e[i][j] = INF;
			}
		}
	}

	for(i = 1; i <= m; ++i)
	{
		scanf("%d %d %d", &q1, &q2, &q3);
		e[q1][q2] = q3;
	}

	for(i = 1; i <= n; ++i)
	{
		dis[i] = INF;
	}
	dis[1] = 0;              /// 初始化dis[1]為0,其他為∞

	head = tail = 1;
	que[head] = 1;           /// 1號頂點入隊
	book[1] = 1;
	tail++;
	while(head < tail)
	{
		for(i = 1; i <= n; ++i)
		{
			if(e[que[head]][i] != INF && dis[i] > dis[que[head]] + e[que[head]][i])
			{
				dis[i] = dis[que[head]] + e[que[head]][i];
				if (!book[i])               /// 頂點不在佇列,加入佇列
				{
					book[i] = 1;
					que[tail++] = i;
				}
			}
		}

		book[que[head]] = 0;    /// 重新標記不在佇列
		head++;                 /// 相當於出隊
	}

	for(i = 1; i <= n; ++i)
	{
		printf("%d ", dis[i]);
	}
	printf("\n");

	system("pause");
	return 0;
}