邻接表

邻接表:所谓邻接表(adjacency list),就是把从同一个顶点发出的边链接在同一个称为边链表的单链表中。边链表的每个结点代表一条边,称为边结点。每个边结点有2 个域:该边终点的序号,以及指向下一个边结点的指针。在邻接表中,还需要一个用于存储顶点信息的顶点数组。例如,图1.19(a)所示的有向图对应的邻接表如图(b)所示。在顶点数组中,每个元素有两个成员:一个成员用来存储顶点信息;另一个成员为该顶点的边链表的表头指针,指向该顶点的边链表。如果没有从某个顶点发出的边,则该顶点没有边链表,因此表头指针为空,如图1.19(b)中的顶点G。在该图中,如果指针为空,则用符号“∧”表示。


图1.19有向图的邻接表

在邻接表每个顶点的边链表中,各边结点所表示的边都是从该顶点发出的边,因此这种邻接表也称为出边表。采用邻接表存储图时,求顶点的出度很方便,只需要统计每个顶点的边链表中边结点的个数即可,但在求顶点的入度时就比较麻烦。在图1.19(b)中,顶点B 的边链表有3 个边结点,分别表示边<B, F>、<B, E>和<B, C>,因此顶点B 的出度为3;顶点C 的边链表中只有1 个边结点,表示边<C, E>,因此顶点C 的出度为1。如果需要统计各顶点的入度,可以采用逆邻接表存储表示图。所谓逆邻接表,也称为入边表,就是把进入同一个顶点的边链接在同一个边链表中。


图1.20有向图的逆邻接表

例如,图1.20(a)所示的有向图对应的逆邻接表如图(b)所示。在图1.20(b)中,顶点B 的边链表有2 个边结点,分别表示边<E, B>和<A, B>,因此顶点B 的入度为2;顶点C 的边链表中也有2个边结点,分别表示边<D, C>和<B, C>,因此顶点C 的入度也为2。

接下来以有向图为例介绍邻接表的实现方法。为了方便求解顶点的出度和入度,在实现时,把出边表和入边表同时包含在图的邻接表结构中。有向图的邻接表用一个结构体LGraph 存储表示,其中包含3 个成员:顶点数组vertexs,顶点数vexnum 和边的数目arcnum,其中顶点数组vertexs 中每个元素都是VNode 结构体变量。VNode 结构体变量存储图中每个顶点,它包含3 个成员:顶点信息、出边表的表头指针和入边表的表头指针,其中后面两个成员都是ArcNode 结构体类型的指针。ArcNode 结构体存储边链表中的边结点,它包含2 个成员:边的另一个邻接点的序号,以及指向下一个边结点的指针。

例题:

问题描述:用邻接表存储有向图,并输出各顶点的出度和入度。

输入描述输入文件中包含多个测试数据,每个测试数据描述了一个无权有向图。每个测试数据的第一行为两个正整数n 和m,1≤ n ≤ 100,1≤ m ≤ 500,分别表示该有向图的顶点数目和边数,顶点的序号从1 开始计起。接下来有m 行,每行为两个正整数,用空格隔开,分别表示一条边的起点和终点。每条边出现一次且仅一次,图中不存在自身环和重边。输入文件最后一行为0 0,表示输入数据结束。

输出描述:

对输入文件中的每个有向图,输出两行:第1 行为n 个正整数,表示每个顶点的出度;第2行也为n 个正整数,表示每个顶点的入度。每两个正整数之间用一个空格隔开,每行的最后一个正整数之后没有空格。

样例输入:

4 7
1 4
2 1
2 2
2 3
2 3
4 2
4 3

0 0

样例输出:

1 4 0 2

1 2 3 1

代码实现:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#define INF 99999999
using namespace std;
const int maxn=105;
struct ArcNode{//边结点
    int adjvex;//有向边的另一个邻接点的序号
    ArcNode *nextarc;//指向下一个边结点的指针
};
struct VNode{//顶点
    int data;//顶点信息
    ArcNode *head1;//出边表的表头指针
    ArcNode *head2;//入边表的表头指针
};
struct LGraph{//图的邻接表存储结构
    VNode vertexs[maxn];//顶点数组
    int vexnum,arcnum;//顶点数和边数
}LG;
void CreateLG(){
    ArcNode *p;//用来构造边链表的边结点指针
    int v1,v2;

    //LG.vexnum=LG.arcnum=0;
    //scanf("%d %d",&LG.vexnum,&LG.arcnum);

    for(int i=0;i<LG.vexnum;i++){//初始化表头指针为空
        LG.vertexs[i].head1=LG.vertexs[i].head2=NULL;
    }
    for(int i=0;i<LG.arcnum;i++){
        scanf("%d %d",&v1,&v2);
        v1--,v2--;
        p = new ArcNode;//假定有足够空间
        p->adjvex = v2;
        p->nextarc = LG.vertexs[v1].head1;//插入链表
        LG.vertexs[v1].head1=p;
        p = new ArcNode;
        p->adjvex = v1;
        p->nextarc = LG.vertexs[v2].head2;
        LG.vertexs[v2].head2=p;
    }
}
void DeleteLG(){
    ArcNode *p;//用来指向边链表中各边结点的指针
    for(int i=0;i<LG.vexnum;i++){
        p=LG.vertexs[i].head1;//释放第i个顶点出边表各边节点所占的存储空间
        while(p!=NULL){
            LG.vertexs[i].head1=p->nextarc;
            delete p;
            p=LG.vertexs[i].head1;
        }
        p=LG.vertexs[i].head2;//释放第i个顶点入边表各边节点所占的存储空间
        while(p!=NULL){
            LG.vertexs[i].head2=p->nextarc;
            delete p;
            p=LG.vertexs[i].head2;
        }
    }
}
int main()
{
    int id,od;//顶点的入度跟出度
    ArcNode *p;//用来遍历边链表的边结点指针
    while(scanf("%d %d",&LG.vexnum,&LG.arcnum)){
        if(LG.arcnum+LG.vexnum==0)
            break;
        CreateLG();
        for(int i=0;i<LG.vexnum;i++){//统计各顶点出度并输出
            od=0;
            p=LG.vertexs[i].head1;
            while(p!=NULL){
                od++;
                p=p->nextarc;
            }
            if(i==0) printf("%d",od)
                ;
            else printf(" %d",od);
        }
        printf("
");
        for(int i=0;i<LG.vexnum;i++){//统计各顶点入度并输出
            id=0;
            p=LG.vertexs[i].head2;
            while(p!=NULL){
                id++;
                p=p->nextarc;
            }
            if(i==0) printf("%d",id);
            else printf(" %d",id);
        }
        printf("
");
        DeleteLG();//释放
    }
    return 0;
}

邻接表的简单实现

typedef struct  
{  
    int to;  
    int w;  
    int next;  
}Edge;  
Edge e[MAX];  
int pre[MAX];  
//初始化  
memset(pre,-1,sizeof(pre));  
//输入  
scanf("%d %d %d",&from,&to,&w1);  
e[i].to = to; 
e[i].w = w1; 
e[i].next = pre[from]; 
pre[from] = i++;  

上面这段代码中,边的结构体Edge由三个元素组成:弧头结点序号,边权值,下一条边的序号。e[i]指的是第i条边。pre[i]记录的是从当前输入的情况来看,序号为i的弧尾结点发出的第一条边的序号是pre[i]。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注