zoukankan      html  css  js  c++  java
  • 图——图的邻接链表存储结构

    1,邻接矩阵法中的残留问题:

           1,MatrixGraph 无法动态添加/删除顶点;

           2,空间使用率低;

     

                 

    2,改进基本思想:

           1,为了进一步提高空间效率,可以考虑使用链表替换数组,将邻接矩阵变换为邻接链表;

           2,占用空间就是因为邻接矩阵的问题,没有连接也要占用四个字节的空间,可以考虑无连接不占用空间的情况;

           3,数组在定义的时候要指明有多少个元素,这样可能导致浪费,可以用链表,需要的时候再增加,不需要预定义一共有多少个元素;

             

    3,邻接链表法:

           1,图中的所有顶点按照编号存储于同一个链表中;

                  1,原来存储在数组中,这里存储在链表中;

           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,邻接链表法实现的图能够支持动态添加和删除顶点;

  • 相关阅读:
    20155328 《网络攻防》 实验一:PC平台逆向破解(5)M
    20155328 《信息安全系统设计基础》 课程总结
    构建之法
    20155327 2017-2018-2《Java程序设计》课程总结
    20155327 实验五 网络编程与安全
    20155327 网络对抗 实验
    20155327 Exp9 Web安全基础
    20155327 EXP8 Web基础
    20155327 实验四 Android程序设计
    20155327 李百乾 Exp7 网络欺诈防范
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10926291.html
Copyright © 2011-2022 走看看