自己之前一直使用邻接矩阵存图,很容易理解,一直听说过邻接表的存图法,但一直没有用过,后来才用了,这里简单说一下。

   为什么要使用邻接矩阵,我个人的理解就是对于稀疏图,使用邻接矩阵太浪费空间,遍历也浪费时间,所以就要使用邻接表。这里就说一下我个人的一些理解。

   邻接表的存的是边,对边进行编号,1号边,2号边,3号边.....再在每条边上记录边的顶点以及权值等信息。以下图为例

图片说明

    默认各个边权值均为1,添加顺序为 1 2,1 4,1 3,3 4,2 4。依次为1号边,2号边,以此类推。我们记录下了边,还要记录下顶点等信息。

   首先我们需要一个head数组,来记录由某一个顶点发出的任意一条边的编号,其次需要一个结构体来记录终点,权值等信息。

struct Node{
    int v=0;                        //终点
    int val=0;                      //权值
    int next=0;                     //下一条边的编号
    Node(){v=0;val=0;next=0;}
}node[200100];

   然后我们看一下添加操作。

void addage(int u,int v,int val){               //起点u 终点v 权值 val
    node[++top].v=v;
    node[top].val=val;
    node[top].next=head[u];
    head[u]=top;
}

   top可以从一开始,或者0开始,都可以,表示第一条边的编号。然后每次进来一个起点,终点,权值。就意味着有一条新的边加入,所以首先top++,然后记录权值,然后第四行的代码node[top].next表示该条边的下一条边(两条边均由一个顶点发出),而head[u]在上一次添加以u为起点的边时,head[u]就记录下了该条边的编号,所以node[top].next=head[u];就记录下了下一条边的编号。

   以我们上边的图为例,首先加入点1 2,即1号边,此时:

    node[1].v=2;
    node[1].val=1;
    node[1].next=head[1];          //此时head[1]=0
    head[1]=1;            //这里的1为1号顶点,与上边的1号边注意区分

   然后我们加入点1 4,即2号边,此时:

    node[2].v=4;
    node[2].val=1;
    node[2].next=head[1];          //此时head[1]=1
    head[1]=2;            //这里的1为1号顶点,上边的2是2号边

   然后我们加入点1 3,即3号边,此时:

    node[3].v=3;
    node[3].val=1;
    node[3].next=head[1];          //此时head[1]=2
    head[1]=3;            //这里的1为1号顶点,上边的3是3号边

   讲到这里应该差不多明白加入边时的操作了,下面看一下遍历,无论是bfs还是dfs,都有一个起点,然后遍历这个起点能到达的所有的边,在这里,我们对于任意一个起点u,可以采用如下方法遍历:

for(int i=head[u];i;i=node[i].next){            //遍历某个顶点的所有边
            int d=node[i].v;                    //该条边的终点
            if(){
                ......                            //其他操作
            }
        }

   相信大家也发现这种写法的缺点了,如果是连通图还好,如果不连通,岂不是要遍历所有的head。。。确实存在这个问题,以后。在学习下,改进下。