鏈式前向星實現以及它的遍歷


  乍一聽,鏈式前向星這個名字很屌。實際上就是鄰接表的靜態實現。

  它的優點是節省了分配內存的時間,效率更高。

  鏈式前向星的構成由一個結構體(包括目標點、邊權值和下一個同起點的邊)和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 }

 

轉載請聲明出處以及作者,謝謝!


免責聲明!

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



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