图——图的邻接矩阵存储结构

1,基本思想: 

       1,用一维数组存储顶点:描述顶点相关的数据;

       2,用二维数组存储边:描述顶点间的关系和权;

      

2,邻接矩阵法(二维数组存储权值表示边):

       1,设图 A = (V, E) 是一个有 n 个顶点的图,图的邻接矩阵为 Edge[n][n],则:

 

       2,解决工程问题时,习惯于对图中的每个顶点进行编号,当不需要权值时,取 w 非空表示结点间有连接;

       3,无向图的邻接矩阵是对称的,有向图的邻接矩阵可能不是对称的;

      

3,邻接矩阵设计与实现:

 

       1,如何具体表示顶点集数组?

       2,如何具体表示边集数组?

      

4,实现方式一:直接使用数组表示顶点集和边集:

       

       1,将邻接矩阵直接搬到代码中,这种方法不科学;

       2,分析下面代码的效率:

           

              1,构造效率低下:

                     1,图对象初始化时,频繁调用顶点类型和边类型的构造函数;

              2,空间使用率低下:

                     1,图对象占用大量空间,而大多数空间处于闲置状态;

              3,无法表示空值:

                     1,无法用统一的方式表示边为空的情形;

   

5,实现方式二:使用指针数组表示顶点集和边集:

     

       1,构造效率:

              1,初始化图对象时,只需要将数组元素赋值为空,不需要调用顶点类型       和边类型的构造函数;

              2,空间使用率:

                     1,顶点数据元素和边数据元素在需要时动态创建;

              3,空值的表示:

                     1,任意的顶点类型和边类型都是用 NULL 表示空值;

6,图的邻接矩阵结构:

  1 #ifndef MATRIXGRAPH_H
  2 #define MATRIXGRAPH_H
  3 
  4 #include "Graph.h"
  5 #include "Exception.h"
  6 #include "DynamicArray.h"
  7 
  8 namespace DTLib
  9 {
 10 
 11 /* MatirxGraph 无法动态 添加/删除 顶点,图对象空间利用率低。不管顶点是否连接,都会占用空间,使用数组浪费空间,用链表替换数组,将邻接矩阵换为邻接链表法;适用于内存资源富足场合 */
 12 template < int N, typename V, typename E >  // N 是图中顶点个数,V 是与顶点相关的数据类型,E 是权值的类型;
 13 class MatrixGraph : public Graph<V, E>
 14 {
 15 protected:
 16     V* m_vertexes[N];  // 每个成员都是指向顶点的相关联的数据元素的指针,这里的数据是工程需要、工程相关的背景;
 17     E* m_edges[N][N];  // 邻接矩阵
 18    int m_eCount;  // 当前图中边的个数
 19 
 20 public:
 21     MatrixGraph()
 22     {
 23         for(int i=0; i<vCount(); i++)  // 指针数组初始化
 24         {
 25             m_vertexes[i] = NULL;  // 没有一个数据元素和顶点相关联
 26             for(int j=0; j<vCount(); j++)
 27             {
 28                 m_edges[i][j] = NULL;  // 一条边也没有
 29             }
 30         }
 31 
 32         m_eCount = 0;
 33    }
 34 
 35     /* 获取和顶点 i 相关联的元素的值 */
 36     V getVertex(int i)  // O(1)
 37     {
 38         V ret;
 39 
 40         if( !getVertex(i, ret) )
 41         {
 42             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
 43         }
 44 
 45         return ret;
 46    }
 47 
 48     /* 获取顶点 i 所关联的元素值 value */
 49     bool getVertex(int i, V& value)  // O(1)
 50     {
 51         bool ret = ( (0 <= i) && (i < vCount()) );  // i 的合法性
 52 
 53         if( ret )
 54         {
 55             if( m_vertexes[i] != NULL ) // 有具体元素关联
 56             {
 57                 value = *(m_vertexes[i]);
 58             }
 59             else
 60             {
 61                 THROW_EXCEPTION(InvalidOperationException, "No value assigned to this vertex ...");
 62             }
 63         }
 64         return ret;
 65    }
 66 
 67     /* 设置和顶点相关联的元素 */
 68     bool setVertex(int i, const V& value)  // O(1)
 69     {
 70         bool ret = ( (0 <= i) && (i < vCount()) );
 71 
 72         if( ret )
 73         {
 74             V* data = m_vertexes[i];  // 中间数据 data 为了异常安全
 75 
 76             if( data == NULL )  // 没有具体元素关联
 77             {
 78                 data = new V();
 79             }
 80 
 81             if( data != NULL )  // 申请成功
 82             {
 83                 *data = value;  // 设置值,单位和要加指针?如果这里发生异常安全,则下面代码不执行,图还是正常的
 84 
 85                 m_vertexes[i] = data;
 86             }
 87             else
 88             {
 89                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to store new vertex value ...");
 90             }
 91         }
 92 
 93         return ret;
 94    }
 95 
 96     /* 获取从 i 作为起点的可达的顶点的编号,到数组中 */
 97     SharedPointer< Array<int> > getAdgacent(int i)  // O(n)
 98     {
 99         DynamicArray<int>* ret = NULL;  // 定义返回值对象
100 
101         if( (0 <= i) && (i < vCount()) )  // i 取值要合法
102         {
103             int n = 0;
104 
105             for(int j=0; j<vCount(); j++)  // 查找 i 行关联点
106             {
107                 if( m_edges[i][j] != NULL )  // 顶点 i 和 j 相关联
108                 {
109                     n++;  // n 表示与 i 相关联的顶点个数
110                 }
111             }
112 
113             ret = new DynamicArray<int>(n);  // 创建返回值对象
114 
115             if( ret != NULL )  // 创建成功
116             {
117                 for(int j=0, k=0; j<vCount(); j++)  // k 作为辅助变量
118                 {
119                     if( m_edges[i][j] != NULL )
120                     {
121                         ret->set(k++, j);  // 记录对应的顶点编号到数组对象中
122                     }
123                 }
124             }
125             else
126             {
127                 THROW_EXCEPTION(NoEnoughMemoryException, "No memory to create ret object ...");
128             }
129         }
130         else
131         {
132             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
133         }
134 
135         return ret;
136    }
137 
138     /* 判断 i 到 j 顶点边是否连接,值不为空就连接 */
139     bool isAdjacent(int i, int j)
140     {
141         return (0 <= i) && (i < vCount()) && (0 <= j) && (j < vCount()) && (m_edges[i][j] != NULL);
142    }
143 
144     /* 获取顶点 i 到顶点 j 边上面的权值 */
145     E getEdge(int i, int j)  // O(1)
146     {
147         E ret;
148 
149         if( !getEdge(i, j, ret) )  // 直接获得值
150         {
151             THROW_EXCEPTION(InvalidParameterException, "Index <i, j> is invalid ...");
152         }
153 
154         return ret;
155    }
156 
157     /* 获取顶点 i 到顶点 j 边上面的权值 */
158     bool getEdge(int i, int j, E& value)  // O(1)
159     {
160         bool ret = ( (0 <= i) && (i < vCount()) &&
161                      (0 <= j) && (j < vCount()) );  // i,j 的合法性
162 
163         if( ret )
164         {
165             if( m_edges[i][j] != NULL ) // 权值存在,可以拿到
166             {
167                 value = *(m_edges[i][j]);  // 获取元素,这里面存储的是指针,所以要加 *
168             }
169             else
170             {
171                 THROW_EXCEPTION(InvalidOperationException, "No value assigened to this edge ...");
172             }
173         }
174 
175         return ret;
176    }
177 
178     /* 设置边上面权值的操作 */
179     bool setEdge(int i, int j, const E& value)  // O(1)
180     {
181         bool ret = ( (0 <= i) && (i < vCount()) &&
182                      (0 <= j) && (j < vCount()) );  // i,j 的合法性
183 
184         if( ret )
185         {
186             E* ne = m_edges[i][j];  // 使用中间变量
187 
188             if( ne == NULL )  // i 到 j 本来不可达
189             {
190                 ne = new E();  // 创建可达对象,设置 i 到 j 的连接
191 
192                 if( ne != NULL )  // 创建成功
193                 {
194                     *ne = value;  // 设置权值
195                     m_edges[i][j] = ne;  // i 到 j 是可达的
196                     m_eCount++;  // 边数加 1
197                 }
198                 else
199                 {
200                     THROW_EXCEPTION(NoEnoughMemoryException, "No memory to store new edge value ...");
201                 }
202             }
203             else
204             {
205                 *ne = value;  // 改变权值
206             }
207         }
208 
209         return ret;
210    }
211 
212     /* 删除 i 和 j 之间的关联,即没有边了 */
213     bool removeEdge(int i, int j)  // O(1)
214     {
215         bool ret = ( (0 <= i) && (i < vCount()) &&
216                      (0 <= j) && (j < vCount()) );  // i,j 的合法性
217 
218         if( ret )
219         {
220             E* toDel = m_edges[i][j];  // 定义中间变量指针指向要删除的权值对象
221             m_edges[i][j] = NULL;  // 中间指针指向要删除的对象了,将邻接矩阵对应元素赋为空值
222 
223             if( toDel != NULL )
224             {
225                 m_eCount--;  // 边个数减一
226                 delete toDel; // 为了尽量的达到异常安全目的,释放 m_edges 所指向的对象;
227             }
228         }
229 
230         return ret;
231    }
232 
233     /* 获取当前图中顶点的数量 */
234     int vCount()  // O(1)
235     {
236         return N;  // 顶点的数量为当前模板参数 N,顶点数量是一个定制;
237    }
238 
239     /* 获取当前图中边的数量 */
240     int eCount()  // O(1)
241     {
242        return m_eCount;  // 添加边和删除边其都相应的变化
243    }
244 
245     /* 顶点 i 的出度,i 行不为 0 的个数 */
246     int OD(int i)  // O(n)
247     {
248         int ret = 0;
249         if( (0 <= i) && (i < vCount()) )
250         {
251             for(int j=0; j<vCount(); j++)  // 数邻接矩阵对应的行中有多少个不为 0 的个数
252             {
253                 if( m_edges[i][j] != NULL ) // 第 i 行不为 0
254                 {
255                     ret++;
256                 }
257             }
258         }
259         else
260         {
261             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
262         }
263 
264         return ret;
265    }
266 
267     /* 顶点 i 的入度,i 列不为 0 的个数 */
268     int ID(int i)  // O(n)
269     {
270         int ret = 0;
271 
272         if( (0 <= i) && (i < vCount()) )
273         {
274             for(int j=0; j<vCount(); j++)  // 数邻接矩阵对应的列中有多少个不为 0 的个数
275             {
276                 if( m_edges[j][i] != NULL ) // 第 i 列不为 0
277                 {
278                     ret++;
279                 }
280             }
281         }
282         else
283         {
284             THROW_EXCEPTION(InvalidParameterException, "Index i is invalid ...");
285         }
286 
287         return ret;
288    }
289 
290 
291     ~MatrixGraph()  // O(n*n)
292     {
293         for(int i=0; i<vCount(); i++)  // 指针数组初始化
294         {
295             for(int j=0; j<vCount(); j++)
296             {
297                 delete m_edges[i][j];  // 销毁每条边
298             }
299 
300             delete m_vertexes[i] ;  // 销毁和每个顶点相关联的数据元素
301         }
302     }
303 };
304 
305 }
306 
307 #endif // MATRIXGRAPH_H

                    

7,图的邻接矩阵结构实现测试代码:

 1 #include <iostream>
 2 #include "MatrixGraph.h"
 3 
 4 using namespace std;
 5 using namespace DTLib;
 6 
 7 int main()
 8 {
 9    MatrixGraph<3, int, int> g;
10 
11     g.setEdge(0, 1, 1);
12     g.setEdge(1, 0, 2);
13     g.setEdge(1, 2, 3);
14 
15     cout << "vCount: " << g.vCount() << endl;
16     cout << "eCount: " << g.eCoutn() << endl;
17 
18     cout << "ID(1): " << g.ID(1) << endl;
19     cout << "OD(1): " << g.OD(1) << endl;
20     cout << "TD(1): " << g.TD(1) << endl;
21 
22     cout << "W(0, 1): " << g.getEdge(0, 1) << endl;
23     cout << "W(1, 0): " << g.getEdge(1, 0) << endl;
24     cout << "W(1, 2): " << g.getEdge(1, 2) << endl;
25 
26     SharedPointer< Array<int> > aj = g.getAdgacent(2);
27 
28     for(int i=0; i<aj->length(); i++)
29     {
30         cout << (*aj)[i] << " ";
31     }
32 
33     cout << endl;
34 
35     cout << "Delete Edge: " << endl;
36 
37     g.removeEdge(0, 1);
38 
39     cout << "eCount: " << g.eCoutn() << endl;
40 
41     g.setVertex(0, 100);
42 
43     cout << "V(0): " << g.getVertex(0) << endl;
44 
45     //cout << "W(0, 1): " << g.getEdge(0, 1) << endl;  // 非法操作
46 
47     return 0;
48 }

8,小结:

       1,邻接矩阵法使用数组对图相关的数据进行存储;

       2,一维数组存储顶点相关的数据(空表示无相关的数据);

       3,二维数组存储边相关的数据(空表示顶点间无连接);

       4,代码实现时使用指针数组进行数据的存储(提高效率);

猜你喜欢

转载自www.cnblogs.com/dishengAndziyu/p/10926281.html