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


    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,代码实现时使用指针数组进行数据的存储(提高效率);

  • 相关阅读:
    Rocket
    Rocket
    Rocket
    Rocket
    Rocket
    Rocket
    UVa 10534 DP LIS Wavio Sequence
    LA 4256 DP Salesmen
    HDU 2476 区间DP String painter
    HDU 4283 区间DP You Are the One
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10926281.html
Copyright © 2020-2023  润新知