乍一聽,鏈式前向星這個名字很屌。實際上就是鄰接表的靜態實現。
它的優點是節省了分配內存的時間,效率更高。
鏈式前向星的構成由一個結構體(包括目標點、邊權值和下一個同起點的邊)和head數組(用於存放某點的第一條出邊),必要的時候還可以添加一個統計入度的數組,因為進行BFS DFS的時候是依靠點的出度和出邊的鄰接關系來進行的。假如有多於一個點的入度為0,那么將只能遍歷到其中一個點以及往后的內容。
對於鏈式前向星,總的一句話:鏈式前向星每添加一條邊就會更新head數組,使得head數組存放的總是最新添加的某點的出邊,此出邊的next總指向head數組之前存放的出邊的序號。
關於鏈式前向星的聲明和初始化:
1 typedef struct Edge { 2 int v; //到達點 3 int w; //邊權值 4 int next;//當前起點的下一條邊的起始edge的序號 5 Edge() { next = -1; } 6 Edge(int vv, int ww) : v(vv), w(ww) { next = -1; } 7 }Edge; 8 9 const int maxn = 1111111; 10 11 int n, m; //n個點標號為1-n,有m條邊 12 int vis[maxn]; //用於標記某邊是否被遍歷到,用於解決環的問題 13 14 int cnt; //邊的數量 15 int dig[maxn];//有一種特殊情況:某點的入度為0,這樣利用邊與出度點的關系是便利不到的,因此我們特殊考慮。統計點的入度 16 int head[maxn]; //每個頂點的邊集數組的第一個存儲位置 17 Edge edge[maxn];//鏈式前向星存儲邊集 18 19 void init() { //每次添加邊的時候,head存儲的都是起點添加的最后一條邊 20 memset(vis, 0, sizeof(vis)); 21 memset(edge, 0, sizeof(edge)); 22 memset(dig, 0, sizeof(dig)); 23 memset(head, -1, sizeof(head)); //因edge從0計數,用於區分 24 cnt = 0; 25 }
接下來考慮添加邊的方式,每次更新cnt位置的結構體,並且更新head數組的對應值:
1 void adde(int uu, int vv, int ww) { //添加邊 2 dig[vv]++; //邊指向點入度加一 3 edge[cnt].v = vv; 4 edge[cnt].w = ww; 5 edge[cnt].next = head[uu]; //使要添加的邊的指向下一條邊的變量存下當前head中對應點的數 6 head[uu] = cnt++; //記下當前邊在edge數組的位置,並且作為頭傳遞給head數組 7 }
還有BFS和DFS,利用鏈式前向星的點和邊的關系,很容易地得出遍歷的方式。與鄰接表非常相似:
1 //根據鏈式前向星的特性,每個邊存的是同一個起點出發的下一條邊, 2 //只需要遍歷每一個點再從每一個點開始的頭邊向后掃描即可按照bfs的順序遍歷所有的邊 3 void bfs_edge() { 4 int s = 1; 5 queue<int> q; 6 while(!q.empty()) q.pop(); 7 for(int i = 1; i <= n; i++) { 8 if(head[i] != -1) { 9 s = i; 10 break; 11 } 12 } 13 for(int i = 1; i <= n; i++) { //特判入度為0的點,也遍歷到。 14 if(!dig[i] && i != s) { 15 // printf("%d ", i); 16 q.push(i); 17 } 18 } 19 q.push(s); //找到第一個出度不為0的點后,作為起點並入棧。 20 memset(vis, 0, sizeof(vis));//初始化vis數組 21 while(!q.empty()) { 22 int u = q.front(); q.pop(); 23 for(int i = head[u]; ~i; i=edge[i].next) { //遍歷每一條以u為起點的邊 24 if(!vis[i]) { //如果當前邊未遍歷到 25 vis[i] = 1; //設置為已遍歷過 26 printf("from %d to %d w %d\n", u, edge[i].v, edge[i].w);//輸出遍歷結果 27 q.push(edge[i].v);//將此邊的目標點放入隊列 28 } 29 } 30 } 31 } 32 33 //與bfs_edge同理,不過遍歷到每一個邊的時候,用vis數組對遍歷到的邊節點 34 //所指向的下一個點進行標記就可以對點進行bfs了 35 void bfs_vertex() { 36 printf("BFS the vertex:\n"); 37 int s = 1; 38 queue<int> q; 39 while(!q.empty()) q.pop(); 40 for(int i = 1; i <= n; i++) { 41 if(head[i] != -1) { 42 s = i; 43 break; 44 } 45 } 46 for(int i = 1; i <= n; i++) { //特判入度為0的點,也遍歷到。 47 if(!dig[i] && i != s) { 48 printf("%d ", i); 49 } 50 } 51 q.push(s); //找到第一個出度不為0的點后,作為起點並入棧。 52 memset(vis, 0, sizeof(vis));//初始化vis數組 53 printf("%d ", s); 54 while(!q.empty()) { 55 int u = q.front(); q.pop(); //取頭隊列頭部的點,進行遍歷 56 vis[u] = 1; //記下當前點為遍歷到 57 for(int i = head[u]; ~i; i=edge[i].next) { //遍歷此點的出邊 58 if(!vis[edge[i].v]) { //如果出邊目標點未被遍歷到 59 vis[edge[i].v] = 1; //設置為已遍歷 並輸出遍歷結果 60 printf("%d ", edge[i].v); 61 q.push(edge[i].v); //將此點放入隊中 62 } 63 } 64 } 65 printf("\n"); 66 } 67 68 void _dfs(int u) {//dfs的輔助函數,用於遞歸遍歷最深處的點 69 vis[u] = 1; //此點為遍歷到,設置為已遍歷 70 for(int i = head[u]; ~i; i=edge[i].next) {//遍歷所有此點的出邊 71 if(!vis[edge[i].v]) { //如果下一個點未被遍歷到 72 vis[edge[i].v] = 1; //設置為已遍歷 73 _dfs(edge[i].v); //遞歸地調用,以此點為起點向下尋找未被遍歷到的點 74 } 75 } 76 printf("%d ", u); //此處輸出,因為dfs是先輸出最深處的點 77 } 78 79 void dfs_vertex() { 80 printf("DFS the vertex:\n"); 81 int s = 1; 82 for(int i = 1; i <= n; i++) { 83 if(head[i] != -1) { 84 s = i; 85 break; 86 } 87 } //查找第一個出度非零的點並執行dfs的輔助函數 88 for(int i = 1; i <= n; i++) { //特判入度為0的點,也遍歷到。 89 if(!dig[i] && i != s) { 90 printf("%d ", i); 91 } 92 } 93 memset(vis, 0, sizeof(vis)); 94 _dfs(s); 95 }
BFS DFS中關於非s的入度為0的頂點的遍歷應該在遍歷結束以后進行遍歷比較合理,不改了QAQ
鏈式前向星對於最短路問題可以有很好的效果,尤其是稀疏圖上。
給出鏈式前向星+dijkstra+堆優化的代碼(poj1511):
1 #include <algorithm> 2 #include <iostream> 3 #include <iomanip> 4 #include <cstring> 5 #include <climits> 6 #include <complex> 7 #include <fstream> 8 #include <cassert> 9 #include <cstdio> 10 #include <bitset> 11 #include <vector> 12 #include <deque> 13 #include <queue> 14 #include <stack> 15 #include <ctime> 16 #include <set> 17 #include <map> 18 #include <cmath> 19 20 using namespace std; 21 22 typedef struct Edge { 23 int v; 24 int w; 25 int next; 26 Edge() { next = -1; } 27 Edge(int vv, int ww) : v(vv), w(ww) { next = -1; } 28 friend bool operator <(Edge e1, Edge e2) { 29 return e1.w < e2.w; 30 } 31 }Edge; 32 33 typedef pair<int, int> PII; 34 typedef long long ll; 35 const ll inf = 0x7fffffff; 36 const int maxn = 1000010; 37 38 int n, m; 39 int cnt; 40 int head[maxn]; 41 int head1[maxn]; 42 ll d[maxn]; 43 ll d1[maxn]; 44 Edge e1[maxn]; 45 Edge e2[maxn]; 46 47 priority_queue<PII, vector<PII>, greater<PII> > pq; 48 49 void init() { 50 memset(e1, 0, sizeof(e1)); 51 memset(e2, 0, sizeof(e2)); 52 memset(head, -1, sizeof(head)); 53 memset(head1, -1, sizeof(head1)); 54 cnt = 0; 55 } 56 57 void adde(Edge* e1, int* head, int uu, int vv, int ww) { 58 e1[cnt].v = vv; 59 e1[cnt].w = ww; 60 e1[cnt].next = head[uu]; 61 head[uu] = cnt++; 62 } 63 64 void dijkstra(int s, Edge* e1, int* head, ll* d) { 65 for(int i = 0; i <= n; i++) d[i] = inf; 66 while(!pq.empty()) pq.pop(); 67 d[s] = 0; 68 pq.push(PII(0, s)); 69 while(!pq.empty()) { 70 PII cur = pq.top(); pq.pop(); 71 int v = cur.second; 72 if(d[v] < cur.first) continue; 73 for(int i = head[v]; ~i; i=e1[i].next) { 74 int w = e1[i].w; 75 if(d[e1[i].v] > d[v] + w) { 76 d[e1[i].v] = d[v] + w; 77 pq.push(PII(d[e1[i].v], e1[i].v)); 78 } 79 } 80 } 81 } 82 83 int main() { 84 // freopen("in", "r", stdin); 85 int T; 86 int uu, vv, ww; 87 scanf("%d", &T); 88 while(T--) { 89 scanf("%d %d", &n, &m); 90 init(); 91 for(int i = 0; i < m; i++) { 92 scanf("%d %d %d", &uu, &vv, &ww); 93 adde(e1, head, uu, vv, ww); 94 adde(e2, head1, vv, uu, ww); 95 } 96 dijkstra(1, e1, head, d); 97 dijkstra(1, e2, head1, d1); 98 ll ans = 0; 99 for(int i = 1; i <= n; i++) { 100 ans += d[i] + d1[i]; 101 } 102 printf("%I64d\n", ans); 103 } 104 }
轉載請聲明出處以及作者,謝謝!
