圖的三種存儲方式


一、鄰接矩陣

適用:
稠密圖,就是說點數的平方與邊數接近的情況,換句話說就是邊特別多。

不適用:
稀疏圖,就是點數的平方與邊數差的特別多,邊數少,但點數多,就不行了,因為空間占用太大了。

實現代碼

#include <bits/stdc++.h>

using namespace std;

const int N = 1010; //圖的最大點數量
int n;
int v[N][N];        //鄰接矩陣
/**
 * 測試數據
 4
 0 5 2 3
 5 0 0 1
 2 0 0 4
 3 1 4 0
 */
int main() {
    cin >> n;
    //讀入到鄰接矩陣
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> v[i][j];

    //下面的代碼將找到與點i有直接連接的每一個點以及那條邊的長度
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            if (v[i][j]) cout << "edge from point " 
                << i << " to point " << j << " with length " << v[i][j] << endl;
    return 0;
}

二、鄰接表

#include <bits/stdc++.h>

using namespace std;

const int N = 1010; //圖的最大點數量
struct Edge {       //記錄邊的終點,邊權的結構體
    int to;         //終點
    int value;      //邊權
};
int n, m; //表示圖中有n個點,m條邊
vector<Edge> p[N];  //使用vector的鄰接表

/**
 * 測試數據
 4 6
 2 1 1
 1 3 2
 4 1 4
 2 4 6
 4 2 3
 3 4 5
 */
int main() {
    cin >> n >> m;
    //m條邊
    for (int i = 1; i <= m; i++) {
        int u, v, l;                //點u到點v有一條權值為l的邊
        cin >> u >> v >> l;
        p[u].push_back({v, l});
    }

    //輸出
    for (int i = 1; i <= n; i++) {
        printf("出發點:%d ", i);
        for (int j = 0; j < p[i].size(); j++)
            printf(" 目標點:%d,權值:%d;", p[i][j].to, p[i][j].value);
        puts("");
    }

    return 0;
}

三、鏈式前向星

鏈式前向星是鄰接表存圖的第二種方法,它自己還有兩種寫法,比用向量存圖的那種鄰接表要快

它是一種以邊為主的存圖方式,\(idx\)表示最后一條邊的預存入的房間號, \(head[i]\)表示以\(i\)為起點第一條邊的房間號。

每條邊有三個屬性:

  • \(head[i]\)出發到哪個結點的邊?
  • 這條邊的邊權是多少?
  • 這條邊的下一條邊是誰?(下一條邊的房間號)

鏈式前向星有三種變形,需要同學們都掌握,找一種自己最喜歡的背下來,其它兩種要求能看懂,因為其它人寫題解,可能使用了其它方式。

1. AcWing方式(純數組)

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;     //點數最大值
int n, m;               //n個點,m條邊

//idx是新結點加入的數據內索引號
//h[N]表示有N條單鏈表的頭,e[M]代表每個節點的值,ne[M]代表每個節點的下一個節點號
int h[N], e[N << 1], ne[N << 1], w[N << 1], idx;

//鏈式前向星
void add(int a, int b, int l) {
    e[idx] = b, ne[idx] = h[a], w[idx] = l, h[a] = idx++;
}


/**
 * 測試數據
 4 6
 2 1 1
 1 3 2
 4 1 4
 2 4 6
 4 2 3
 3 4 5
 */
int main() {
    cin >> n >> m;
    //初始化為-1,每個頭節點寫成-1
    memset(h, -1, sizeof h);

    //m條邊
    for (int i = 1; i <= m; i++) {
        int u, v, l;                //點u到點v有一條權值為l的邊
        cin >> u >> v >> l;
        //加入到鏈式前向星
        add(u, v, l);
    }

    //遍歷每個結點
    for (int i = 1; i <= n; i++) {
        printf("出發點:%d ", i);
        for (int j = h[i]; j != -1; j = ne[j])
            printf(" 目標點:%d,權值:%d;", e[j], w[j]);
        puts("");
    }
    return 0;
}

2. 結構體+數組

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;     //點數最大值
int n, m, idx;          //n個點,m條邊,idx是新結點加入的數據內索引號

//鏈式前向星
struct Edge {
    int to;     //到哪個結點
    int value;  //邊權
    int next;   //同起點的下一條邊的編號
} edge[N << 1]; //同起點的邊的集合 N<<1就是2*N,一般的題目,邊的數量通常是小於2*N的,這個看具體的題目要求

int head[N];    //以i為起點的邊的集合入口處

//加入一條邊,x起點,y終點,value邊權
void add_edge(int x, int y, int value) {
    edge[++idx].to = y;         //終點
    edge[idx].value = value;    //權值
    edge[idx].next = head[x];   //以x為起點上一條邊的編號,也就是與這個邊起點相同的上一條邊的編號
    head[x] = idx;              //更新以x為起點上一條邊的編號
}

/**
 * 測試數據
 4 6
 2 1 1
 1 3 2
 4 1 4
 2 4 6
 4 2 3
 3 4 5
 */
int main() {
    cin >> n >> m;

    //m條邊
    for (int i = 1; i <= m; i++) {
        int u, v, l;                //點u到點v有一條權值為l的邊
        cin >> u >> v >> l;
        //加入到鏈式前向星
        add_edge(u, v, l);
    }

    //遍歷每個結點
    for (int i = 1; i <= n; i++) {
        printf("出發點:%d ", i);
        for (int j = head[i]; j; j = edge[j].next)  //遍歷每個結點的每一條邊
            printf(" 目標點:%d,權值:%d;", edge[j].to, edge[j].value);
        puts("");
    }
    return 0;
}

3. 結構體+數組(2)

為什么鏈式前向星有兩種實現方法呢?這其實是看\(idx\)用不用\(0\)的問題,如果它用了\(0\),那么就是在加邊的最后需要++,如果不用\(0\),進來就++。

第二個變化就是如果用了\(0\),那么\(0\)就不能用做默認值了,所以需要初始化memset(head,-1 ,sizeof head);

第三個變化就是遍歷時的條件變了,成了j!=-1,而不用\(0\)的就是j就行了,我個人還是喜歡用不帶\(0\)的那個,就是上面的。是因為網上好多網友喜歡這種方式,如果我們看其它人的題解時,可能看不懂,所以也要了解一下。

#include <bits/stdc++.h>

using namespace std;
const int N = 1010;     //點數最大值
int n, m, idx;          //n個點,m條邊,idx是新結點加入的數據內索引號

//鏈式前向星
struct Edge {
    int to;     //到哪個結點
    int value;  //邊權
    int next;   //同起點的下一條邊的編號
} edge[N << 1]; //同起點的邊的集合 N<<1就是2*N,一般的題目,邊的數量通常是小於2*N的,這個看具體的題目要求

int head[N];    //以i為起點的邊的集合入口處

//加入一條邊,x起點,y終點,value邊權
void add_edge(int x, int y, int value) {
    edge[idx].to = y;           //終點
    edge[idx].value = value;    //權值
    edge[idx].next = head[x];   //以x為起點上一條邊的編號,也就是與這個邊起點相同的上一條邊的編號
    head[x] = idx++;            //更新以x為起點上一條邊的編號
}

/**
 * 測試數據
 4 6
 2 1 1
 1 3 2
 4 1 4
 2 4 6
 4 2 3
 3 4 5
 */
int main() {
    cin >> n >> m;

    //初始化head數組
    memset(head, -1, sizeof head);

    //m條邊
    for (int i = 1; i <= m; i++) {
        int u, v, l;                //點u到點v有一條權值為l的邊
        cin >> u >> v >> l;
        //加入到鏈式前向星
        add_edge(u, v, l);
    }

    //遍歷每個結點
    for (int i = 1; i <= n; i++) {
        printf("出發點:%d ", i);
        for (int j = head[i]; j != -1; j = edge[j].next)  //遍歷每個結點的每一條邊
            printf(" 目標點:%d,權值:%d;", edge[j].to, edge[j].value);
        puts("");
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM