📜  使用最短路径更快算法检测图中的负循环(1)

📅  最后修改于: 2023-12-03 14:49:55.469000             🧑  作者: Mango

使用最短路径更快算法检测图中的负循环

什么是负循环

负循环又称负环,是指在有向图中,存在一条从某个顶点开始经过若干边后回到该顶点的回路,并且其中所有边的权值之和为负数。

检测负循环的算法

使用最短路径更快算法(SPFA)可以检测图中的负循环。

最短路径更快算法是一种单源最短路径算法,其基本思想是利用队列对图中所有节点进行松弛操作,直到每个节点的最短路径长度稳定下来。

具体实现过程如下:

  1. 初始化距离数组dist为INF,将起点s的dist设为0,将起点s加入队列queue中;
  2. 当队列不为空时,取出队首节点u;
  3. 对于u的每个邻接节点v,若dist[v]>dist[u]+w(u,v),则更新dist[v]为dist[u]+w(u,v),并将节点v加入队列queue中;
  4. 如果有节点被更新了dist,则重复步骤2和3;
  5. 如果没有节点被更新了dist,则最短路径已经求出。

如果经过一定次数的松弛操作后仍然有节点的dist被更新,则说明图中存在负循环。

代码示例

以下代码为C++实现的SPFA算法,用于检测图中是否存在负循环:

#define INF 0x3f3f3f3f // INF为一个足够大的数
vector<pair<int,int> > edge[N]; // 存储边的数组
int dist[N], cnt[N]; // dist为起点到各点的最短距离,cnt为各点进入队列的次数
bool inq[N]; // 判断一个点是否在队列中

bool SPFA(int s) { // s为起点
    memset(dist, INF, sizeof(dist)); // 距离数组初始化为INF
    memset(cnt, 0, sizeof(cnt)); // 进入队列次数初始化为0
    memset(inq, false, sizeof(inq)); // 判断是否在队列中初始化为false
    queue<int> q;
    q.push(s);
    inq[s] = true;
    dist[s] = 0;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;
        for (int i = 0; i < edge[u].size(); i++) { // 遍历每个邻接点
            int v = edge[u][i].first;
            int w = edge[u][i].second;
            if (dist[v] > dist[u] + w) { // 如果从当前点松弛到邻接点的距离更短
                dist[v] = dist[u] + w;
                cnt[v] = cnt[u]+1;
                if (cnt[v] > n) return true; // 如果进入队列的次数超过n次,说明存在负环
                if (!inq[v]) { // 如果邻接点不在队列中,则加入队列
                    q.push(v);
                    inq[v] = true;
                }
            }
        }
    }
    return false; // 如果没有负环,返回false
}

以上代码中,N为最大节点数,n为图中节点个数。edge数组存储每个节点的所有邻接边,pair表示邻接的节点及边权。cnt数组用于记录每个节点进入队列的次数,如果某个节点进入队列的次数超过n次,则说明图中存在负循环。在每次对节点进行松弛操作后,需要将节点加入队列中,如果节点已经在队列中则不加入,防止重复遍历。