鄰接表

W_K_KAI發表於2024-04-12

鄰接表

感覺寫的很好啊!
轉載自:陣列模擬鄰接表 - AcWing

首先假設我們有n個點(n <= N),m條邊(m <= M)。

我們可以想一下對於任意一個結點u, 需要記錄鄰邊的哪些資訊。
這些資訊應該包括這條鄰邊的終點,權重,以及下一條鄰邊的編號。
注意這裡不需要記錄鄰邊的起點,因為我們使用的時候都是給出起點的。
所以我們可以定義一個struct來表示鄰邊:

struct Edge
{
    int eid;    // 該條邊的編號
    int e;      // 該條邊的終點
    int w;      // 該條邊的權重
    int nxt;    // 下一條鄰邊的編號
};

如果我們用上面的資料結構來記錄鄰邊的資訊,那麼我們只需要定義如下變數來表示鄰接表:

// 注意N和M的區別
int h[N];
Edge edges[M];
int eidx;

由於每條邊都記錄了下一條邊的編號,這樣我們只要把每個結點的第一條鄰邊的編號記錄在h陣列,我們就可以遍歷它的每一條鄰邊了。

如果我們把Edge裡的資訊分開存到不同陣列裡,那麼我們可以得到平時我們看到的變數定義:

// 注意N和M的區別
int h[N];
int e[M], w[M], nxt[M];  // 這三個陣列等價於之前的Edge edges[M],注意這些陣列的下標表示鄰邊的編號
int eidx;

這裡每個陣列的下標的含義不一樣。
關鍵的事情說三遍:h陣列的下標為結點的編號,e,w,nxt陣列的下標為邊的編號,eidx為邊的編號
關鍵的事情說三遍:h陣列的下標為結點的編號,e,w,nxt陣列的下標為邊的編號,eidx為邊的編號
關鍵的事情說三遍:h陣列的下標為結點的編號,e,w,nxt陣列的下標為邊的編號,eidx為邊的編號

如果理解了以上,下面就很好理解了。
有向圖的鄰接表儲存就是對於每個點 u 對應一個頭節點h[u],記錄第一條鄰邊的編號。
e, w, nxt陣列的編號和建圖的順序有關,對於某一個點u, 它的所有鄰邊的編號不一定是連續的。
nxt[eidx]=h[u]; h[u]=eidx; 這個操作就是把新建的邊插入表頭。(先把新建的邊的next指向現在隊頭的next,然後更新隊頭的next)
然後再eidx++, 給下一次建邊使用

下邊用圖模擬一下加入四條邊的過程

  1. 初始狀態

Screen Shot 2020-12-02 at 7.49.05 AM.png

  1. 加完第一條邊(1,2,9)之後

Screen Shot 2020-12-02 at 7.49.43 AM.png

  1. 加完第二條邊(2,4,1)之後

Screen Shot 2020-12-02 at 7.49.49 AM.png

  1. 加完第三條邊(1,3,3)之後
    這裡可以看到後加入的邊,反而在鄰接表的最前面

Screen Shot 2020-12-02 at 7.50.02 AM.png

  1. 加完第四條邊(3,4,5)之後

Screen Shot 2020-12-02 at 7.50.10 AM.png

最後是程式碼及註釋

const int N = 1010, M = 1010;

int h[N], e[M], w[M], nxt[M], eidx;

void add(int u, int v, int weight)   // 新增有向邊 u->v, 權重為weight
{
    e[eidx] = v;        // 記錄邊的終點
    w[eidx] = weight;   // 記錄邊的權重
    nxt[eidx] = h[u];   // 將下一條邊指向結點u此時的第一條邊
    h[u] = eidx;        // 將結點u的第一條邊的編號改為此時的eidx
    eidx++;             // 遞增邊的編號edix, 為將來使用
}

void iterate(int u)   // 遍歷結點u的所有鄰邊
{
    // 從u的第一條邊開始遍歷,直到eid==-1為止
    for(int eid = h[u]; eid != -1; eid = nxt[eid])
    {
        int v = e[eid];
        int weight = w[eid];
        cout << u << "->" << v << ", weight: " << weight << endl;
    }
}

int main()
{
    int n, m;
    cin >> n >> m;

    memset(h, -1, sizeof h);  // 初始化h陣列為-1
    eidx = 0;                 // 初始化邊的編號為0

    while(m--)
    {
        int u, v, weight;
        cin >> u >> v >> weight;
        add(u, v, weight);
    }

    for(int u = 1; u <= n; ++u)
        iterate(u);

    return 0;
}


相關文章