zoukankan      html  css  js  c++  java
  • 算法8:巧妙的邻接表(数组实现)

    之前我们介绍过图的邻接矩阵存储法,它的空间和时间复杂度都是N2,如今我来介绍第二种存储图的方法:邻接表,这样空间和时间复杂度就都是M。

    对于稀疏图来说,M要远远小于N2。

    先上数据。例如以下

    1
    2
    3
    4
    5
    6
    4 5
    1 4 9
    4 3 8
    1 2 5
    2 4 6
    1 3 7

    091650e0f00oqrcjcfnq93.png

    第一行两个整数m

    n表示顶点个数(顶点编号为1~n),m表示边的条数。接下来m行表示,每行有3个数x y z表示顶点x到顶点y的边的权值为z下图就是一种使用链表来实现邻接表的方法。

    091650gyll6hbqbjyxls8s.png


    上面这样的实现方法为图中的每一个顶点(左边部分)都建立了一个单链表(右边部分这样我们就能够通过遍历每一个顶点的链表。从而得到顶点全部的边了使用链表来实现邻接表对于痛恨指针的的朋友来说,这简直就是噩梦。

    这里我将为大家介绍一种使用数组来实现邻接表这是一种在实际应用中很easy实现的方法。这样的方法为每一个顶点ii1~n保存了一个类似链表的东西。里面保存的是从顶点i出发的全部的边,详细例如以下。


    首先我们依照读入的顺序为每一条边进行编号1~m

    比方第一条边“1 4 9”的编号就是1,“1 3 7”这条边的编号是5


    这里uvw三个数组用来记录每条边的详细信息,即u[i]v[i]w[i]表示第i条边是从第u[i]号顶点到v[i]号顶点(u[i]àv[i],且权值为w[i]


    091650h35zq3wgx30x3oe3.png

    再用一个first数组来存储每一个顶点当中一条边的编号以便待会我们来枚举每顶点全部的边(你可能存储当中一条边的编号就能够了?不可能吧,每一个顶点都须要存储其全部边的编号才行吧!甭着急。继续往下看比方1号顶点有一条边是 “1 4 9”(该条边的编号是1),那么就将first[1]的值设为1。假设某个顶点i没有以该顶点为起始点的边,则将first[i]的值设为-1。如今我们来看看详细怎样操作,初始状态例如以下。


    091650zw3988qpj5iljj8g.png


    咦?上图中怎么多了一个next数组。有什么作用呢?不着急。待会再解释。如今先读入第一条边“1 4 9”。


    读入第1条边1 4 9,将这条边的信息存储到u[1]v[1]w[1]

    同一时候这条边赋予一个编号由于这条边是最先读入的,存储在uvw数组下标为1的单元格中,因此编号就是1。这条边的起始点是1号顶点。因此将first[1]的值设为1


    另外这条编号为1的边1号顶点u[1]为起始点第一条边,所以next[1]的值设为-1。也是说,假设当前这条编号为i的边,是我们发现的u[i]起始点的第一条边。就将next[i]的值设为-1(貌似的这个next数组非常神奇啊⊙_

    091651kwo5g0aycy07wfwd.png


    读入第2条边4 3 8将这条边的信息存储到u[2]v[2]w[2]中。这条边的编号为2

    这条边的起始顶点是4号顶点,因此将first[4]的值设为2另外这条“编号为2的边”是我们发现以4号顶点为起始点的第一条边,所以将next[2]的值设为-1

    091651fkswcj34c05k8w4k.png


    读入第3条边1 2 5将这条边的信息存储到u[3]v[3]w[3]中。这条边的编号为3,起始顶点是1号顶点。我们发现1号顶点已经有一条编号为的边了。假设此时将first[1]的值设为3那“编号为1的边岂不是丢失了?我有办法,此时仅仅需将next[3]的值设为1就可以。如今你知道next数组是用来做什么的吧。

    next[i]存储的是“编号为i的边”的“一条边”的编号。

    091651df28foy9ct7fl7qf.png


    读入第4条边(2 4 6),将这条边的信息存储到u[4]v[4]w[4]中,这条边的编号为4起始顶点是2号顶点,因此将first[2]的值设为4

    另外这条“编号为4的边”是我们发现以2号顶点为起始点的第一条边。所以将next[4]的值设为-1

    091652vf4eg69f5zfsese9.png


    读入第5条边(1 3 7),将这条边的信息存储到u[5]v[5]w[5]中,这条边的编号为5,起始顶点1号顶点。此时须要first[1]的值设为5并将next[5]的值改为3

    091652li6mzammza242tmp.png


    此时,假设我们想遍历1号顶点的每一条边就非常easy1号顶点的当中一条边的编号存储在first[1]中。其余的边则能够通过next数组寻找到请看下图

    091652rtjh5qe2211eee58.png

                                                                                 算法8:巧妙的邻接表(数组实现)


    细心的同学会发现,此时遍历边某个顶点边的时候的遍历顺序正好与读入时候的顺序相反。

    由于为每一个顶点插入边的时候都直接插入链表的首部而不是尾部。只是这并不会产生不论什么问题。这正是这样的方法的其妙之处


    创建邻接表的代码例如以下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int n,m,i;
    //u、v和w的数组大小要依据实际情况来设置,要比m的最大值要大1
    int u[6],v[6],w[6];
    //first和next的数组大小要依据实际情况来设置,要比n的最大值要大1
    int first[5],next[5];
    scanf("%d %d",&n,&m);
    //初始化first数组下标1~n的值为-1,表示1~n顶点临时都没有边
    for(i=1;i<=n;i++)
        first[i]=-1;
    for(i=1;i<=m;i++)
    {
        scanf("%d %d %d",&u[i],&v[i],&w[i]);//读入每一条边
        //以下两句是关键啦
        next[i]=first[u[i]];
        first[u[i]]=i;
    }


    接下来怎样遍历每一条边呢?我们之前说过事实上first数组存储的就是每一个顶点ii1~n)的第一条边。比方1号顶点的第一条边是编号为5的边(1 3 7)。2号顶点的第一条边是编号为4的边(2 4 6)。3号顶点没有出向边,4号顶点的第一条边是编号为2的边(2 4 6)。那么怎样遍历1号顶点的每一条边呢?也非常easy。

    请看下图:


    遍历1号顶点全部边的代码例如以下。

    1
    2
    3
    4
    5
    6
    k=first[1];// 1号顶点当中的一条边的编号(事实上也是最后读入的边)
    while(k!=-1) //其余的边都能够在next数组中依次找到
    {
        printf("%d %d %d ",u[k],v[k],w[k]);
        k=next[k];
    }


    遍历每一个顶点的全部代码例如以下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    for(i=1;i<=n;i++)
    {
        k=first[i];
        while(k!=-1)
        {
            printf("%d %d %d ",u[k],v[k],w[k]);
            k=next[k];
        }
    }


            能够发现使用邻接表来存储图的时间空间复杂度是O(M),遍历每一条边的时间复杂度是也是O(M)。假设一个图是稀疏图的话,M要远小于N2

    因此稀疏图选用邻接表来存储要比邻接矩阵来存储要好非常多。

       欢迎转载,码字不easy啊,转载麻烦注明出处

       【啊哈!算法】系列8:巧妙的邻接表(数组实现)  http://ahalei.blog.51cto.com/4767671/1391988


  • 相关阅读:
    遇到项目上面有叉,但是找不到错误的原因
    遇到build的问题
    遇到scan configurtation CDT builder等的错误
    遇到attemp to invoke virtual method
    遇到looper之类关于消息循环的
    Linux与Windows信息交互快捷方法
    并行查询
    PostgreSQL 事务管理的MVCC
    Linux安装memcached
    Linux 安装 Redis
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6920209.html
Copyright © 2011-2022 走看看