zoukankan      html  css  js  c++  java
  • 图基本算法 图的表示方法 邻接矩阵 邻接表

      

      要表示一个图G=(V,E),有两种标准的表示方法,即邻接表和邻接矩阵。这两种表示法既可用于有向图,也可用于无向图。通常采用邻接表表示法,因为用这种方法表示稀疏图(图中边数远小于点个数)比较紧凑。但当遇到稠密图(|E|接近于|V|^2)或必须很快判别两个给定顶点手否存在连接边时,通常采用邻接矩阵表示法,例如求最短路径算法中,就采用邻接矩阵表示。

      图G=<V,E>的邻接表表示是由一个包含|V|个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点。对于每一个u∈V,邻接表Adj[u]包含所有满足条件(u,v)∈E的顶点v。亦即,Adj[u]包含图G中所有和顶点u相邻的顶点。每个邻接表中的顶点一般以任意顺序存储。

      如果G是一个有向图,则所有邻接表的长度之和为|E|,这是因为一条形如(u,v)的边是通过让v出现在Adj[u]中来表示的。如果G是一个无向图,则所有邻接表的长度之和为2|E|,因为如果(u,v)是一条无向边,那么u会出现在v的邻接表中,反之亦然。邻接表需要的存储空间为O(V+E)。

      邻接表稍作变动,即可用来表示加权图,即每条边都有着相应权值的图,权值通常由加权函数w:E→R给出。例如,设G=<V,E>是一个加权函数为w的加权图。对每一条边(u,v)∈E,权值w(u,v)和顶点v一起存储在u的邻接表中。

    邻接表C++实现:

      1 #include <iostream>
      2 #include <cstdio>
      3 using namespace std;
      4 
      5 #define maxn 100  //最大顶点个数
      6 int n, m;       //顶点数,边数
      7 
      8 struct arcnode  //边结点
      9 {
     10     int vertex;     //与表头结点相邻的顶点编号
     11     int weight = 0;     //连接两顶点的边的权值
     12     arcnode * next; //指向下一相邻接点
     13     arcnode() {}
     14     arcnode(int v,int w):vertex(v),weight(w),next(NULL) {}
     15     arcnode(int v):vertex(v),next(NULL) {}
     16 };
     17 
     18 struct vernode      //顶点结点,为每一条邻接表的表头结点
     19 {
     20     int vex;    //当前定点编号
     21     arcnode * firarc;   //与该顶点相连的第一个顶点组成的边
     22 }Ver[maxn];
     23 
     24 void Init()  //建立图的邻接表需要先初始化,建立顶点结点
     25 {
     26     for(int i = 1; i <= n; i++)
     27     {
     28         Ver[i].vex = i;
     29         Ver[i].firarc = NULL;
     30     }
     31 }
     32 
     33 void Insert(int a, int b, int w)  //尾插法,插入以a为起点,b为终点,权为w的边,效率不如头插,但是可以去重边
     34 {
     35     arcnode * q = new arcnode(b, w);
     36     if(Ver[a].firarc == NULL)
     37         Ver[a].firarc = q;
     38     else
     39     {
     40         arcnode * p = Ver[a].firarc;
     41         if(p->vertex == b)  //如果不要去重边,去掉这一段
     42         {
     43             if(p->weight < w)
     44                 p->weight = w;
     45             return ;
     46         }
     47         while(p->next != NULL)
     48         {
     49             if(p->next->vertex == b)    //如果不要去重边,去掉这一段
     50             {
     51                 if(p->next->weight < w);
     52                     p->next->weight = w;
     53                 return ;
     54             }
     55             p = p->next;
     56         }
     57         p->next = q;
     58     }
     59 }
     60 void Insert2(int a, int b, int w)   //头插法,效率更高,但不能去重边
     61 {
     62     arcnode * q = new arcnode(b, w);
     63     if(Ver[a].firarc == NULL)
     64         Ver[a].firarc = q;
     65     else
     66     {
     67         arcnode * p = Ver[a].firarc;
     68         q->next = p;
     69         Ver[a].firarc = q;
     70     }
     71 }
     72 
     73 void Insert(int a, int b)   //尾插法,插入以a为起点,b为终点,无权的边,效率不如头插,但是可以去重边
     74 {
     75     arcnode * q = new arcnode(b);
     76     if(Ver[a].firarc == NULL)
     77         Ver[a].firarc = q;
     78     else
     79     {
     80         arcnode * p = Ver[a].firarc;
     81         if(p->vertex == b) return;      //去重边,如果不要去重边,去掉这一句
     82         while(p->next != NULL)
     83         {
     84             if(p->next->vertex == b)    //去重边,如果不要去重边,去掉这一句
     85                 return;
     86             p = p->next;
     87         }
     88         p->next = q;
     89     }
     90 }
     91 void Insert2(int a, int b)   //头插法,效率跟高,但不能去重边
     92 {
     93     arcnode * q = new arcnode(b);
     94     if(Ver[a].firarc == NULL)
     95         Ver[a].firarc = q;
     96     else
     97     {
     98         arcnode * p = Ver[a].firarc;
     99         q->next = p;
    100         Ver[a].firarc = q;
    101     }
    102 }
    103 void Delete(int a, int b)   //删除以a为起点,b为终点的边
    104 {
    105     arcnode * p = Ver[a].firarc;
    106     if(p->vertex == b)
    107     {
    108         Ver[a].firarc = p->next;
    109         delete p;
    110         return ;
    111     }
    112     while(p->next != NULL)
    113         if(p->next->vertex == b)
    114         {
    115             p->next = p->next->next;
    116             delete p->next;
    117             return ;
    118         }
    119 }
    120 
    121 void Show()     //打印图的邻接表(有权值)
    122 {
    123     for(int i = 1; i <= n; i++)
    124     {
    125         cout << Ver[i].vex;
    126         arcnode * p = Ver[i].firarc;
    127         while(p != NULL)
    128         {
    129             cout << "->(" << p->vertex << "," << p->weight << ")";
    130             p = p->next;
    131         }
    132         cout << "->NULL" << endl;
    133     }
    134 }
    135 
    136 void Show2()     //打印图的邻接表(无权值)
    137 {
    138     for(int i = 1; i <= n; i++)
    139     {
    140         cout << Ver[i].vex;
    141         arcnode * p = Ver[i].firarc;
    142         while(p != NULL)
    143         {
    144             cout << "->" << p->vertex;
    145             p = p->next;
    146         }
    147         cout << "->NULL" << endl;
    148     }
    149 }
    150 int main()
    151 {
    152     int a, b, w;
    153     cout << "Enter n and m:";
    154     cin >> n >> m;
    155     Init();
    156     while(m--)
    157     {
    158         cin >> a >> b >> w;       //输入起点、终点
    159         Insert(a, b, w);        //插入操作
    160         Insert(b, a, w);        //如果是无向图还需要反向插入
    161     }
    162     Show();
    163     return 0;
    164 }
    View Code 

      

      邻接表表示法也有潜在的不足之处,即如果要确定图中边(u,v)是否存在,只能在顶点u邻接表Adj[u]中搜索v,除此之外没有其他更快的办法。这一不足可通过图的邻接矩阵表示法来弥补,但要(在渐进意义下)以占用更多的存储空间为代价。

      在图G=(V,E)的临界矩阵表示法中,假定各顶点按某种任意的方式编号为1,2,···,|V|,那么G的邻接矩阵为一个|V|*|V|的矩阵A=(a[i][j]),它满足:

      观察无向图的邻接矩阵会发现,它是沿主对角线对称的。在一个无向图中,(u,v)和(v,u)表示同一条边,故无向图的邻接矩阵A的转置矩阵就是它本真。在某些应用中,可以只存储邻接矩阵的对角线以及对角线以上的部分,这样一来,图所占用的存储空间几乎可以减少一半。

      邻接矩阵也可以用来表示加权图。例如,如果G=<V,E>是一个加权图,其权值函数为w,对于边(u,v)∈E,其权值w(u,v)就可以简单地存储在邻接矩阵的第u行第v列的元素中。如果边不存在,则可以在矩阵的相应元素中存一个NIL值,在很多问题中,对这样的元素赋0或∞会更为方便些。

    邻接矩阵C++实现:

     1 #include <iostream>
     2 #include <cstdio>
     3 using namespace std;
     4 
     5 #define maxn 100
     6 #define INF 1xffffff    //预定于的最大值
     7 int n, m;   //顶点数、边数
     8 int g[maxn][maxn];      //邻接矩阵表示
     9 
    10 void Init()
    11 {
    12     for(int i = 1; i <= n; i++)
    13         for(int j = 1; j <= n; j++)
    14         g[i][j] = 0;    //讲所有顶点度数置零,若为带权图,则置为INF
    15 }
    16 void Show() //打印邻接矩阵
    17 {
    18     for(int i = 1; i <= n; i++)
    19     {
    20         for(int j = 1; j <= n; j++)
    21             cout << g[i][j] << " ";
    22         cout << endl;
    23     }
    24 }
    25 int main()
    26 {
    27     int a, b;
    28     cout << "Enter n and m:";
    29     cin >> n >> m;
    30     while(m--)
    31     {
    32         cin >> a >> b;  //输入为边的始点、终点,若有权,还需输入权w
    33         g[a][b] = 1;    //a、b间存在边,将g[a][b]置1,若有权,则将其置为权值
    34         g[b][a] = 1;    //对于无向图,还要插入边(b,a)
    35     }
    36     Show();
    37     return 0;
    38 }
    View Code

    (文章以及相关代码参考算法导论编写)

  • 相关阅读:
    [导入]自由的生活
    [导入]宁静
    [导入]书店
    [导入]娶老婆的15条金科玉律
    [导入]静静的日子
    [导入]生活无聊的日子
    [导入]新的任务
    [导入]问题:我是一个内向的男生。请问怎么追求自己喜欢的女孩
    [导入]奋斗
    java 多种方式文件读取
  • 原文地址:https://www.cnblogs.com/dzkang2011/p/graph_1.html
Copyright © 2011-2022 走看看