圖——圖的鄰接鏈表存儲結構

1,鄰接矩陣法中的殘留問題:ios

       1,MatrixGraph 沒法動態添加/刪除頂點;編程

       2,空間使用率低;數組

 

             

2,改進基本思想:函數

       1,爲了進一步提升空間效率,能夠考慮使用鏈表替換數組,將鄰接矩陣變換爲鄰接鏈表;測試

       2,佔用空間就是由於鄰接矩陣的問題,沒有鏈接也要佔用四個字節的空間,能夠考慮無鏈接不佔用空間的狀況;this

       3,數組在定義的時候要指明有多少個元素,這樣可能致使浪費,能夠用鏈表,須要的時候再增長,不須要預約義一共有多少個元素;spa

         

3,鄰接鏈表法:設計

       1,圖中的全部頂點按照編號存儲於同一個鏈表中;3d

              1,原來存儲在數組中,這裏存儲在鏈表中;code

       2,每個頂點對應一個鏈表,用於存儲始發於該頂點的邊;

              1,鏈表中包含的信息見 3;

              2,將數組改爲鏈表;

       3,每一條邊的信息包含:起點,終值,權值;

      

4,無論多高深的設計方法,都是從最基本的設計方法中演變而來的,最基本的設計方法中,都有一些原則性的東西,好比這裏使用了將數組變換成鏈表來節省空間的原則;

  

5,鄰接鏈表的示例:

1,鏈表的鏈表,垂直的是一個鏈表,而鏈表每個數據元素當中還有另外一個鏈表做爲成員而存在;

 

             

6,設計與實現:

 

      

7,邊數據類型的設計:

      

       1,由於鄰接鏈表裏面存儲的就是與邊相關的類型的對象了;

       2,定義一個鄰接鏈表,鏈表裏面存儲的類型爲 Edge;

 

8,頂點數據類型的設計:

     

      

9,動態增長/刪除頂點:

       1,int addVertex();

              1,增長新的頂點,返回頂點編號;

              2,增長新的頂點只能在末尾,由於不能打亂以前已經存在的頂點編號順序;

       2,int addVertex(const V& value);

              1,增長新頂點的同時附加數據元素;

       3,void removeVertex();

              1,刪除最近增長的頂點;

              2,只能從末尾刪除,將最近添加的頂點刪除,不能說刪除一個頂點,其它頂點就亂套了;

      

10,圖的鄰接鏈表結構:

  1 #ifndef LISTGRAPH_H
  2 #define LISTGRAPH_H
  3 
  4 #include "Graph.h"
  5 #include "LinkList.h"
  6 #include "Exception.h"
  7 #include "DynamicArray.h"
  8 
  9 namespace DTLib
 10 {
 11 
 12 /* 適用於內存資源受限場合 */
 13 template < typename V, typename E >
 14 class ListGraph : public Graph<V, E>
 15 {
 16 protected:
 17     /* 定義頂點 */
 18     struct Vertex : public Object
 19     {
 20         V* data;  // 頂點的數據成員
 21         LinkList< Edge<E> > edge;  // 鄰接鏈表保存邊對象
 22         Vertex()
 23         {
 24             data = NULL;
 25         }
 26    };
 27 
 28     /* 定義實際鄰接鏈表 */
 29     LinkList<Vertex*> m_list;  // 實際的鄰接鏈表
 30 public:
 31     /* 構造一個新的有 n 個頂點的 ListGraph 對象 */
 32     ListGraph(unsigned int n = 0)
 33     {
 34         for(unsigned int i=0; i<n; i++)  // 根據參數添加頂點
 35         {
 36             addVertex();  // 根據參數動態的增長頂點
 37         }
 38    }
 39 
 40     /* 動態的增長一個新的頂點 */
 41     int addVertex()  // O(n)
 42     {
 43         int ret = -1;
 44         Vertex* v = new Vertex();  // 建立堆空間對象
 45 
 46         if( v != NULL )
 47         {
 48             m_list.insert(v);  // 將頂點加入這個鏈表 O(n)
 49             ret = m_list.length() - 1;  // 返回新加結點在鏈表裏面的編號,在最後一個位置
 50         }
 51         else
 52         {
 53             THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new vertex object ...");
 54         }
 55 
 56         return ret;
 57    }
 58 
 59     /*動態的增長一個新的頂點的同時,將值設入 */
 60     int addVertex(const V& value)  // O(n)
 61     {
 62         int ret = addVertex();  // 添加結點
 63 
 64         if( ret >= 0 )  // 增長成功
 65         {
 66             setVertex(ret, value);  // 頂點所在 ret 處值爲 value
 67         }
 68 
 69         return ret;
 70    }
 71 
 72     /* 設置頂點 i 處的值爲 value */
 73     bool setVertex(int i, const V& value)  // O(n)
 74     {
 75         int ret = ( (0 <= i) && (i < vCount()) );  // 判斷 i 的合法性
 76 
 77         if( ret )
 78         {
 79             Vertex* vertex = m_list.get(i);  // 將鏈表中 i 位置處元素取出來, O(n)
 80             V* data = vertex->data;  // data 指向具體的頂點數據元素
 81 
 82             if( data == NULL )  // 沒有指向成功
 83             {
 84                 data = new V();  // 動態的建立一個指向頂點相關的數據元素出來
 85             }
 86 
 87             if( data != NULL )  // 建立成功
 88             {
 89                 *data = value;  // 建立成功就將參數值 value 傳遞過去
 90                 vertex->data = data;  // 將 data 保存下來
 91             }
 92             else
 93             {
 94                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create new vertex value ...");
 95             }
 96         }
 97 
 98         return ret;
 99    }
100 
101     /* 將 i 位置處相關聯的頂點數據元素值返回 */
102     V getVertex(int i)  // O(n)
103     {
104         V ret;
105 
106         if( !getVertex(i, ret) )
107         {
108             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
109         }
110 
111         return ret;
112    }
113 
114     /* 將 i 位置處相關聯的頂點數據元素值賦值給引用 value */
115     bool getVertex(int i, V& value)  // O(n)
116     {
117         int ret = ( (0 <= i) && (i < vCount()) );  // 判斷 i 的合法性
118 
119         if( ret )
120         {
121             Vertex* v = m_list.get(i);  // 獲得頂點相關數據 O(n)
122 
123             if( v->data != NULL )  //  頂點關聯數據元素值
124             {
125                 value = *(v->data);  // 關聯就將數據值返回
126             }
127             else  // 頂點沒有關聯數據
128             {
129                 THROW_EXCEPTION(InvalidOperationException, "No value assigned to this vertex ...");
130             }
131         }
132 
133         return ret;
134    }
135 
136     /* 刪除最近添加的頂點 */
137     void removeVertex()  // O(n*n)
138     {
139         if( m_list.length() > 0 )  // 當前圖中有頂點
140         {
141             int index = m_list.length() - 1;  // 刪除最近添加的頂點,省得破壞圖的結構
142             Vertex* v = m_list.get(index);  // 取出頂點相關的數據元素 O(n)
143 
144             if( m_list.remove(index) )  // 首先刪除最後一個頂點
145             {  
146             /* 這裏循環條件的先後分別利用了逗號表達式 */
147                 for(int i=(m_list.move(0), 0); !m_list.end(); i++, m_list.next() )  // 看其餘頂點當中有沒有和這個頂點相關聯的邊,有了要刪除
148                 {   
149                  /* 當前結點臨界鏈表裏,查找與之關聯的邊,邊起點爲 i,終點爲 index,存在則 pos 非負*/
150                     int pos = m_list.current()->edge.find(Edge<E>(i, index));  // O(n),返回存在的下標
151 
152                     if( pos >= 0 )
153                     {
154                         m_list.current()->edge.remove(pos);  // 刪除當前頂點的元素,即相關聯的邊
155                     }
156                 }
157 
158                 delete v->data;  // 釋放對應頂點數據元素值空間
159 
160                 delete v;  // 頂點自身佔用的空間釋放掉
161             }
162         }
163         else
164         {
165             THROW_EXCEPTION(InvalidOperationException, "No vertex in current graph ...");
166         }
167    }
168 
169     /* 獲取從頂點 i 出發能夠抵達的頂點編號,以一個數組的方式返回;遍歷頂點 i 就能夠獲得與之相關的頂點了 */
171     SharedPointer< Array<int> > getAdgacent(int i)  // O(n)
172     {
173         DynamicArray<int>* ret = NULL;
174 
175         if( (0 <= i) && (i < vCount()) )
176         {
177             Vertex* vertex = m_list.get(i);  // 從鏈表中獲取與頂點相關的數據元素 O(n)
178 
179             ret = new DynamicArray<int>(vertex->edge.length());  // 建立返回值數組,個數是鄰接鏈表中邊的個數
180 
181             if( ret != NULL )
182             {  
183            /* 這裏用了兩個逗號表達式 */
184                 for(int k=(vertex->edge.move(0), 0); !vertex->edge.end(); k++, vertex->edge.next())  // O(n)
185                 {
186                     ret->set(k, vertex->edge.current().e);  // 獲取鄰接頂點邊的第二個元素,並將標號設置到要返回的數組中  O(1)
187                 }
188             }
189             else
190             {
191                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret object ...");
192             }
193         }
194         else
195         {
196             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
197         }
198 
199         return ret;
200    }
201 
202     /* 判斷 i 到 j 頂點邊是否鏈接,能找到就是鏈接的 */
203     bool isAdjacent(int i, int j)
204     {
205         return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_list.get(i)->edge.find(Edge<E>(i, j)) >= 0);
206    }
207 
208     E getEdge(int i, int j)  // O(n)
209     {
210         E ret;
211 
212         if( !getEdge(i, j, ret) )
213         {
214             THROW_EXCEPTION(InvalidParameterException, "Edge <i, j> is invalid ...");
215         }
216 
217         return ret;
218    }
219 
220     /* 獲取從 i 到 j 的邊的權值,放在 value 中 */
221     bool getEdge(int i, int j, E& value)
222     {
223         int ret = ( (0 <= i) && (i < vCount()) &&
224                     (0 <= j) && (j < vCount()) );
225 
226         if( ret )  // O(n)
227         {
228             Vertex* vertex = m_list.get(i);  // 獲得頂點 O(n)
229             int pos = vertex->edge.find(Edge<E>(i, j));  // 在頂點的鄰接鏈表中找找是否存在從 i 到 j 的邊,存在則 pos 非 O(n)
230 
231             if( pos >= 0 )
232             {
233                 value = vertex->edge.get(pos).data;  // 取出值O(n)
234             }
235             else
236             {
237                 THROW_EXCEPTION(InvalidOperationException, "No valid assigned to this edge ...");
238             }
239         }
240 
241         return ret;
242    }
243 
244     /* 設置從 i 到 j 的鄰接鏈表的值,存儲在 value 中 */
245     bool setEdge(int i, int j, const E& value)  // O(n)
246     {
247         int ret = ( (0 <= i) && (i < vCount()) &&
248                     (0 <= j) && (j < vCount()) );
249 
250         if( ret )
251         {
252             Vertex* vertex = m_list.get(i);  // 得到頂點 O(n)
253             int pos = vertex->edge.find(Edge<E>(i, j));  // 查找邊是否存在,存在則 pos 非零 O(n)
254 
255             if( pos >= 0 )
256             {
257                 ret = vertex->edge.set(pos, Edge<E>(i, j, value));  // 設置鄰接鏈表當中對應位置處的值,用邊的構造函數直接賦值
258             }
259             else
260             {
261                 ret = vertex->edge.insert(0, Edge<E>(i, j, value));  // 沒有邊時,插入一條邊和邊的權值,至於插入到那個位置,無所謂
262             }
263         }
264 
265         return ret;
266    }
267 
268     /* 刪除從 i 開始抵達 j 的邊;查找對應鄰接鏈表並刪除 */
269     bool removeEdge(int i, int j)  // O(n)
270     {
271         int ret = ( (0 <= j) && (i < vCount()) &&
272                     (0 <= j) && (j < vCount()) );
273 
274         if( ret )
275         {
276             Vertex* vertex = m_list.get(i);  // 取出感興趣的邊O(n)
277 
278             int pos = vertex->edge.find(Edge<E>(i, j));  // 相應頂點中的邊是否存在,存在則 pos 非負 O(n)
279 
280             if( pos >= 0 )
281             {
282                 ret = vertex->edge.remove(pos);  // 刪除對應的點 O(n)
283             }
284         }
285 
286         return ret;
287    }
288 
289     /* 獲取圖中頂點個數,即鏈表中元素個數 */
290     int vCount()  // O(1)
291     {
292         return m_list.length();  // O(1)
293    }
294 
295     /* 獲取邊的個數,獲取全部頂點的鄰邊個數之和*/
296     int eCount()  // O(n)
297     {
298         int ret = 0;
299 
300         for(m_list.move(0); !m_list.end(); m_list.next())
301         {
302             ret += m_list.current()->edge.length();  // 累加當前頂點的鄰邊個數
303         }
304 
305         return ret;
306    }
307 
308     /* 實現頂點 i 的入度函數 */
309     int ID(int i)  // O(n*n)
310     {
311         int ret = 0;
312 
313         if( (0 <= i) && (i < vCount()) )
314         {
315             for(m_list.move(0); !m_list.end(); m_list.next())
316             {
317                 LinkList< Edge<E> >& edge = m_list.current()->edge;  // 定義當前鄰接鏈表別名,方便編程
318                 for(edge.move(0); !edge.end(); edge.next())
319                 {
320                     if( edge.current().e == i )  // 多少條終止頂點是頂點 i
321                     {
322                         ret++;
323 
324                         break;  // 每一個頂點到另外一個頂點的邊的個數通常的只有一個,除非兩個頂點見有兩個權值不同的有向邊
325                     }
326                 }
327             }
328         }
329         else
330         {
331             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
332         }
333 
334         return ret;
335    }
336 
337     /* 獲取頂點 i 的出度 */
338     int OD(int i)  // O(n)
339     {
340         int ret = 0;
341 
342         if( (0 <= i) && (i < vCount()) )
343         {
344             ret = m_list.get(i)->edge.length();  // O(n)
345         }
346         else
347         {
348              THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
349         }
350 
351         return ret;
352    }
353 
354     ~ListGraph()
355     {
356         while( m_list.length() > 0 )
357         {
358             Vertex* toDel = m_list.get(0);
359 
360             m_list.remove(0);
361 
362             delete toDel->data;
363 
364             delete toDel;
365         }
366     }
367 };
368 
369 }
370 
371 #endif // LISTGRAPH_H

      

11,圖的鄰接鏈表法的測試代碼:

 1 #include <iostream>
 2 #include "ListGraph.h"
 3 
 4 using namespace std;
 5 using namespace DTLib;
 6 
 7 int main()
 8 {
 9    ListGraph<char, int> g(4);
10 
11     g.setVertex(0, 'A');
12     g.setVertex(1, 'B');
13     g.setVertex(2, 'C');
14    g.setVertex(3, 'D');
15 
16     for(int i=0; i<g.vCount(); i++)
17     {
18         cout << i << " : " << g.getVertex(i) << endl;
19    }
20 
21    ListGraph<char, int> g1;
22 
23     g1.addVertex('A');
24     g1.addVertex('B');
25     g1.addVertex('C');
26    g1.addVertex('D');
27 
28    //    g1.removeVertex();
29 
30     for(int i=0; i<g1.vCount(); i++)
31     {
32         cout << i << " : " << g1.getVertex(i) << endl;
33    }
34 
35     g1.setEdge(0, 1, 5);
36     g1.setEdge(0, 3, 5);
37     g1.setEdge(1, 2, 8);
38     g1.setEdge(2, 3, 2);
39    g1.setEdge(3, 1, 9);
40 
41     cout << "W(0, 1) : " << g1.getEdge(0, 1) << endl;
42     cout << "W(0, 3) : " << g1.getEdge(0, 3) << endl;
43     cout << "W(1, 2) : " << g1.getEdge(1, 2) << endl;
44     cout << "W(2, 3) : " << g1.getEdge(2, 3) << endl;
45    cout << "W(3, 1) : " << g1.getEdge(3, 1) << endl;
46 
47    cout << "eCount : " << g1.eCount() << endl;
48 
49    //    g1.removeEdge(3, 1);
50    //    cout << "W(3, 1) : " << g1.getEdge(3, 1) << endl;
51 
52    cout << "eCount : " << g1.eCount() << endl;
53 
54    SharedPointer< Array<int> > aj = g1.getAdgacent(0);
55 
56     for(int i=0; i<aj->length(); i++)
57     {
58         cout << (*aj)[i] << endl;
59    }
60 
61     cout << "ID(1) : " << g1.ID(1) << endl;
62     cout << "OD(1) : " << g1.OD(1) << endl;
63    cout << "TD(1) : " << g1.TD(1) << endl;
64 
65    g1.removeVertex();
66 
67    cout << "eCount : " << g1.eCount() << endl;
68 
69     cout << "W(0, 1) : " << g1.getEdge(0, 1) << endl;
70    cout << "W(1, 2) : " << g1.getEdge(1, 2) << endl;
71 
72     return 0;
73 }

 

12,同鏈表同樣,鏈表鄰接法對結點的操做也有編號,這個是對圖的結點操做的一個捷徑;

 

13,小結:

       1,鄰接鏈表法使用鏈表對圖相關的數據進行存儲;

              1,從鄰接矩陣改進而來,將數組改進爲鏈表;

       2,每個頂點關聯一個鏈表,用於存儲邊相關的數據;

       3,全部頂點按照編號被組織在同一個鏈表中;

       4,鄰接鏈表法實現的圖可以支持動態添加和刪除頂點;

相關文章
相關標籤/搜索