SPFA和鏈式前向星

管管19發表於2022-11-23

鏈式前向星

一種儲存圖的資料結構

建立一個結構體和一個陣列加一個變數,這裡的to代表邊\((u,v)\)中的\(v\)結點,而\(edge\)陣列的索引\(idx\)代表\(u\),其中\(w\)代表權值,\(next\)代表以\(u\)為起始點的上一個邊。
\(head\)代表這個\(x\)結點在\(edge\)陣列中的最後一個邊的下標索引,\(cnt\)用於記錄邊時當\(edge\)的下標索引用。

struct {
  int to, w, next;
} edge[MAX_N];
int head[MAX_N], cnt = 0;

為鏈式前向星新增邊

  1. ++cnt為新新增的邊選擇一個空變數
  2. edge[++cnt].next=head[u]代表讓\(edge[cnt]\)中的\(next\)變數指向\(u\)結點的上一個邊
  3. \(head[u]=cnt\)代表更新結點\(u\)的最後一條邊在\(edge\)中的下標
edge[++cnt].next=head[u];
edge[cnt].w=w;
edge[cnt].to=v;
head[u]=cnt;

遍歷

首先獲取結點\(x\)的最後一條邊,經過資料處理後,將i移向結點\(x\)的上一條邊

for(int i=head[x];i;i=edge[i].next)

SPFA演算法

求最小單源路徑

  1. 將源點放入佇列中,並標誌源點\(s\)已經在佇列之中\(vis[s]=true\)
  2. 進入一個迴圈,當佇列為空,各節點的最短路徑便求出來了
  3. 從佇列中取出一個結點,並更新標誌,遍歷該結點的邊,對符合條件的各邊\(dis[edge[i].to]>dis[v]+edge[i].w\)進行鬆弛,然後如果符合條件的鬆弛邊目標結點如果未在佇列中,則放入,更改標誌。

鬆弛:對於每個頂點v∈V,都設定一個屬性\(d[v]\),用來描述從源點s到v的最短路徑上權值的上界,稱為最短路徑估計。就是這個操作\(dis[edge[i].to] = dis[v] + edge[i].w;\)

  queue<int> que;
  que.emplace(s);
  vis[s] = true;
  while (!que.empty()) {
    int v = que.front();
    que.pop();
    vis[v] = false;
    for (int i = head[v]; i; i = edge[i].next) {
      if (dis[v] + edge[i].w < dis[edge[i].to]) {
        dis[edge[i].to] = dis[v] + edge[i].w;
        if (!vis[edge[i].to]) {
          que.emplace(edge[i].to);
          vis[edge[i].to] = true;
        }
      }
    }
  }

求是否存在負環

如果一個圖存在負環,那麼其的最短路徑一定會存在一個無限迴圈,經過負環後,路徑越來越小,那麼一定有一些結點,一直入隊出隊,判斷是否有結點入隊次數大於\(n\)

  queue<int> que;
  que.emplace(1);
  vis[1]=true,dis[1]=0;
  while (!que.empty()) {
    int v = que.front();
    que.pop();
    vis[v] = false;
    fe(ver, G[v]) if (dis[ver.to] > dis[v] + ver.cost) {
      dis[ver.to] = dis[v] + ver.cost;
      if (!vis[ver.to]) {
        if (++cnt[ver.to] >= n) {
            //存在負環
        }
        que.emplace(ver.to);
        vis[ver.to] = true;
      }
    }
  }

相關文章