zoukankan      html  css  js  c++  java
  • 图论3——图的存储与基本性质

    本文作者frankchenfu,blogs网址http://www.cnblogs.com/frankchenfu/,转载请保留此文字。

    在数学上,图是表示物件与物件之间联系的数学对象;而在计算机中,每个物件可以抽象成一个节点,而关系就是一条边。

    这里主要介绍图的一些较关键的性质以及邻接矩阵、邻接表的应用。

    1、有向图和无向图

    图分为有向图和无向图。顾名思义,有向图就是每条边都具有方向,一条从$A$->$B$的有向边它可以让一个东西从$A$走到$B$,却不能沿同一条边从$B$走回$A$;反之,无向图就是不具有方向的,既可以从$A$到$B$,也可以沿同一条边从$B$到$A$。一条边可能有一个权值,叫边权。

               有向图                             无向图

    注意到上面这一句话中,我强调了同一条边。这表明,一张图中可能会有重复的边,即起点和终点相同的边(在无向图中可能是起点终点位置调换的边),我们把这样的边成为重边

    如果一张图中,有$n$个结点,同时还有着$n-1$条边,那么这张图事实上是一颗树。

    如果这张图中,从$A$一直沿着某些不重复的边走,然后能走回$A$,那么这张图中就存在着环。一张图中可能存在着很多个环,也可能一个都没有。例如,在上面的有向图中,不存在环,而在无向图中,结点$2,4,7$构成了一个环。

    2、图的存储

    限于篇幅,这里仅介绍最常用的邻接矩阵和邻接表

    2.1 邻接矩阵法

     我们可以构造一个矩阵,矩阵的第$i$行第$j$列(即$g_{i,j}$)表示结点$i$和结点$j$的关系,而没有连边的两个节点,我们就设置为“假想无穷大”。例如,上面的有向图可以表示为(inf即“假想无穷大”):

    这里为第$i$行第$j$列为1表示有连边。大家可以自行验证是否表示上述有向图。用代码表示就可以是 g[i][j]=1; .无向图也可以类似的表示,注意,因为边是无向的,所以一旦第$i$行第$j$列有连边,那么第$j$行第$i$列也一定是有连边的。用代码表示即为 g[i][j]=g[j][i]=1; 。那么邻接矩阵法就讲完了。可是,如果对于这样一个数据范围:

    对于$100$%的数据满足$n le 10^6 , m le 10^6$,其中$n$表示节点数,$m$表示边数。

    如果空间限制是标准的256MB或512MB,即使是1GB,存邻接矩阵也是不够的啊!邻接矩阵的二维数组的空间消耗是O($n^2$)的。注意到有很多无用的空间,也就是上面的inf,事实上比我们有用的空间还多(在浏览上面的表格时你有没有这么想呢?)。因为边的数量较小,于是我们考虑,能不能主要存边的信息,而尽量不存点呢?于是我们的邻接表就出来了。

    2.2 邻接表法

    邻接表的思想就是存边的信息,而不是点的信息。我们给每一条边一个编号。

    仍然对于上面的有向图,我们邻接表里存的内容可以这么表示:(其中冒号前的数字表示表示这一条边的编号)

    邻接表存的就是这么一个东西。它首先每个节点都有存一个“从这条边出发的第一条边”,然后每一条边除了保存自身的信息(包括到哪里去,权值等)以外,还有指向下一条边的编号。这让我们想起了什么?对,链表!它每个节点内存的内容就很像链表,然后下一条边指向0就表示结束了,这个节点的边就遍历忘了。这样也是可以存储一个图的。这种方法的优点就是空间复杂度上的优势,它的空间复杂度(如果不考虑每个节点存的“第一条边”的话)是O($m$)的。那么对于上面的数据范围就可以很轻松的解决了。其中fir数组存的是从$i$点连出的编号最大的边。

    可是这种方法也有缺点,例如判断点之间是否联通,那么查找最坏情况下要O($m$)的复杂度,而邻接矩阵只需要O($1$)。

    接下来给出两种存图方法的Cpp代码:

    #include<cstdio>
    #include<cstring>
    //邻接矩阵
    const int MAXN=3010;
    int g[MAXN][MAXN];//graph
    int n,m;
    
    //在一般情况下,u和v分别表示边的起点和终点,w表示权值
    void init()
    {
        memset(g,0x7f,sizeof(g));//inf
        for(int i=1;i<=n;i++)
            g[i][i]=0;
    }
    void adde(int u,int v,int w)
    {
        g[u][v]=w;//有向
        g[u][v]=g[v][u]=w;//无向
    }
    #include<cstdio>
    #include<cstring>
    //邻接表
    const int MAXN=100010;
    const int MAXM=200010;//注意,无向图空间双倍!
    
    struct edge
    {
        int to,w,nxt;
    }e[MAXM];
    int fir[MAXN];
    int n,m,tot=0;
    
    void adde(int u,int v,int w)
    {
        e[++tot].to=v;e[tot].w=w;
        e[tot].nxt=fir[u];
        fir[u]=tot;
    }

    打一个广告,我自己的博客中还有使用邻接表储存的堆优化Dijkstra算法,有兴趣的同学可以浏览一下。限于水平,作者所写的难免有疏忽之处,望大家指正,Thanks!

  • 相关阅读:
    return 与 exit() 的区别
    RtlInitUnicodeString
    计算机网络 学习笔记-概论
    STM32学习笔记(五) USART异步串行口输入输出(轮询模式)
    STM32学习笔记(四) RCC外设的学习和理解
    简单RTOS学习(一) uc/os-II 工程模板建立
    web前端学习(一) html+js实现文本框背景及只读属性修改
    TCP/IP协议学习(一) LWIP实现网络远程IAP下载更新
    STM32学习笔记(三) STM32的GPIO的深入学习
    STM32学习笔记(二) 基于STM32-GPIO的流水灯实现
  • 原文地址:https://www.cnblogs.com/frankchenfu/p/7625393.html
Copyright © 2011-2022 走看看