1、圖的儲存
- 設點數為n,邊數為m
1.1、二維陣列
- 方法:使用一個二維陣列 adj 來存邊,其中 adj[u][v] 為 1 表示存在 u到 v的邊,為 0 表示不存在。如果是帶邊權的圖,可以在 adj[u][v] 中儲存u到v的邊的邊權。
- 複雜度:
- 查詢是否存在某條邊:\(O(1)\)
- 遍歷一個點的所有出邊:\(O(n)\)
- 遍歷整張圖:\(O(n^2)\)
- 空間複雜度:\(O(n^2)\)
1.2、鄰接表
-
方法:使用一個支援動態增加元素的資料結構構成的陣列,如 vector< int> adj[n + 1] 來存邊,其中 adj[u] 儲存的是點u的所有出邊的相關資訊(終點、邊權等);
-
複雜度:
- 查詢是否存在u到v的邊:\(O(d^+(u))\)(如果事先進行了排序就可以使用二分查詢做到\(O(log(d^+(u)))\) )。
- 遍歷點u的所有出邊:\(O(d^+(u))\)。
- 遍歷整張圖:O(n+m)。
- 空間複雜度:O(m)。
1.3、直接存邊
- 方法:使用一個陣列來存邊,陣列中的每個元素都包含一條邊的起點與終點(帶邊權的圖還包含邊權)。(或者使用多個陣列分別存起點,終點和邊權。)
struct Edge{ int u,v;//邊的端點 int w;//權重 }Edges[MAXN];
- 複雜度:
- 查詢是否存在某條邊:\(O(m)\)。
- 遍歷一個點的所有出邊:\(O(m)\)。
- 遍歷整張圖:\(O(nm)\)。
- 空間複雜度:\(O(m)\)。
- 由於直接存邊的遍歷效率低下,一般不用於遍歷圖。在Kruskal演算法 中,由於需要將邊按邊權排序,需要直接存邊
1.4、鏈式前向星(本質是用陣列模擬連結串列)
-
方法:本質是用陣列模擬連結串列,主要有兩個陣列
Edges[MAXN] 儲存邊的資訊,包括兩個端點、權重、下一條邊在Edges中的索引; head[MAXN] head[i]為節點i的第一條出邊在Edges中的序號; 在插入邊的時候維護一個tot變數記錄總計的邊的個數
-
複雜度:
- 查詢是否存在u到v的邊:\(O(d^+(u))\)(如果事先進行了排序就可以使用二分查詢做到\(O(log(d^+(u)))\) )。
- 遍歷點u的所有出邊:\(O(d^+(u))\)。
- 遍歷整張圖:O(n+m)。
- 空間複雜度:O(m)。
-
程式碼板子:
#include<bits/stdc++.h> using namespace std; const int MAXN = 1e6; struct Edge {//邊結構體 int u,v;//邊的端點; int w;//權重 int nxt;//下一條邊在Edge中的索引 }Edges[MAXN]; int head[MAXN];//每個節點出邊 int tot;//總的邊數,隨著邊的增加而增加 void init(int n){ tot=0; //初始化head陣列 for(int i=0; i<n; i++) head[i]=-1; //memset(head,-1,sizeof(head)); //memset是以位元組為單位,初始化記憶體塊 //位元組單位的陣列時,可以用memset把每個陣列單元初始化成任何你想要的值 //因為一個int型別的變數佔4個位元組,而memset是將每一個位元組初始化成1, //所以一個int型別的變數被初始化成了0x01010101。而這個數是16843009 //memset初始化int只能初始化0和-1; } void addEdge(int u,int v,int w){ Edges[tot].u=u; Edges[tot].v=v; Edges[tot].w=w; Edges[tot].nxt=head[u]; head[u] = tot; tot++; }
2、圖的遍歷
2.2、dfs(深度優先 depth first search)
- 基於上述的鏈式前向星實現
void dfs(int u) { //v 可以是圖中的一個頂點 //也可以是抽象的概念,如 dp 狀態等,這一點很難想 vis[u] = 1; //標記該節點被訪問過 for (int i = head[u]; i; i = Edges[i].nxt) { if (!vis[Edges[i].v]) { //task to do dfs(v); } } }
2.3、bfs(寬度優先 breadth first search)
- 每次都嘗試訪問同一層的節點。 如果同一層都訪問完了,再訪問下一層。這樣做BFS 演算法找到的路徑是從起點開始的最短合法路徑。換言之,這條路所包含的邊數最小。在 BFS 結束時,每個節點都是通過從起點到該點的最短路徑訪問的。
- 基於上述的鏈式前向星實現:
void bfs(int u){ vector<int> d;//記錄到達各個節點的最近距離; vector<int> p;//記錄最短路上的節點 vector<int> vis(MAXN,0);//0代表節點未被訪問過 queue<int> q; q.push(u); vis[u]=1;//標記訪問 d[u]=0; p[u]=-1; while(!q.empty()){ u=q.front(); q.pop(); for(int i=head[u]; i; i=Edges[i].nxt){ int v = Edges[i].v;//到達的點 if(!vis[v]){ q.push(v); vis[v]=1; d[v]=d[u]+1; p[v]=u;//記錄前序節點 //task to do } } } }
?
❤️