图的存储形式——邻接表

邻接表:邻接表是图的一种链式存储结构。在邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的节点表示依附于顶点vi的边(对有向图是以顶点vi为尾的弧)。每个结点有三个域组成,其中邻接点域指示与顶点vi邻接的点在途中的位置,链域指示下一条边或者弧的结点;数据域存储和边或者弧相关的信息,如权值等。每个链表上附设一个表头结点。在表头结点中,除了设置链域指向链表第一个结点之外,还设置有存储顶点vi的名。如下所示:

实现:

/**************************************
图的存储之邻接表
by Rowandjj
2014/6/23
**************************************/

#include<iostream>
using namespace std;

#define MAX_VERTEX_NUM 20//最大顶点数

typedef enum{DG,DN,AG,AN}GraphKind;//有向图、有向网、无向图、无向网
typedef struct _ARCNODE_//表节点(弧)
{
    int adjvex;//邻接点序号
    struct _ARCNODE_ *nextarc;//指向下一条弧
    int info;//信息(权值)
}ArcNode;

typedef struct _VNODE_//头结点
{
    char data;//顶点名
    ArcNode *firstarc;//指向第一条弧
}VNode,AdjList[MAX_VERTEX_NUM];

typedef struct _ALGRAPH_//邻接表
{
    AdjList vertices;//邻接表
    int vexnum;//顶点数
    int arcnum;//弧数
    GraphKind kind;//图的种类
}ALGraph;

void (*VisitFunc)(char);  //全局函数指针 

bool visited[MAX_VERTEX_NUM]; /* 访问标志数组(全局量) */

void Visit(char p)
{
    cout<<p<<" ";
}
//-----------------操作-------------------------------------
int LocateVex(ALGraph G,char u);//若G中存在顶点u,则返回该顶点在图中位置;否则返回-1
bool CreateGraph(ALGraph* G);//采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图)
void DestroyGraph(ALGraph* G);//销毁图G
char GetVex(ALGraph G,int v);//通过序号v得到顶点名
bool PutVex(ALGraph* G,char v,char value);//对v赋新值value
int FirstAdjVex(ALGraph G,char v);//返回顶点v的第一个邻接顶点的序号
int NextAdjVex(ALGraph G,char v,char w);//返回v的(相对于w的)下一个邻接顶点的序号,若w是v的最后一个邻接点,则返回-1
void InsertVex(ALGraph* G,char v);//在图G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做)
bool DeleteVex(ALGraph* G,char v);//删除G中顶点v及其相关的弧
bool InsertArc(ALGraph* G,char v,char w);//在G中增添弧<v,w>,若G是无向的,则还增添对称弧<w,v>
bool DeleteArc(ALGraph* G,char v,char w);//在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v>
void DFSTravel(ALGraph* G,void (*Visit)(char));//深度优先
void DFS(ALGraph G,int v);
void BFSTravel(ALGraph G,void (*Visit)(char));//广度优先
void Display(ALGraph G);//打印图

//----------------辅助队列------------------------------------------
#define MAX_QUEUE_SIZE 20
typedef struct _QUEUENODE_
{
    int data;
    struct _QUEUENODE_ *next;
}QueueNode;
typedef struct _QUEUE_
{
    QueueNode *pHead;
    QueueNode *pTail;
    int size;
}Queue;

bool InitQueue(Queue *Q);
bool DestroyQueue(Queue *Q);
bool DeQueue(Queue *Q,int* e);
bool EnQueue(Queue *Q, int e);
bool QueueEmpty(Queue Q);
//------------------------------------------------------------------
bool InitQueue(Queue *Q)
{
    Q->pHead = Q->pTail = (QueueNode *)malloc(sizeof(QueueNode));
    if(!Q->pHead)
    {
        return false;
    }
    Q->pHead->next = NULL;
    Q->size = 0;
    return true;
}

bool EnQueue(Queue *Q, int e)
{
    QueueNode *node = (QueueNode*)malloc(sizeof(QueueNode));
    node->data = e;
    node->next = NULL;
    Q->pTail->next = node;
    Q->pTail = node;
    Q->size++;
    return true;
}

bool DeQueue(Queue *Q,int* e)
{
    QueueNode *node = Q->pHead->next;
    if(node)
    {
        *e = node->data;
        Q->pHead->next = node->next;
        if(Q->pTail == node)
        {
            Q->pTail = Q->pHead;
        }
        free(node);

        Q->size--;
    }
    return true;
}
bool QueueEmpty(Queue Q)
{
    return Q.size == 0;
}
bool DestroyQueue(Queue *Q)
{
    QueueNode *pTemp = Q->pHead->next;
    while(pTemp != NULL)
    {
        Q->pHead->next = pTemp->next;
        free(pTemp);
        pTemp = Q->pHead->next;
    }
    free(Q->pHead);
    Q->size = 0;
    return true;
}

//------------------------------------------------------------------
int LocateVex(ALGraph G,char u)
{
    int i;
    for(i = 0; i < G.vexnum; i++)
    {
        if(u == G.vertices[i].data)
        {
            return i;
        }
    }
    return -1;
}

bool CreateGraph(ALGraph* G)
{
    int i,j,k;
    int w;//权值
    char va,vb;//弧尾、弧头
    ArcNode *p;//弧

    cout<<"请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ";
    scanf("%d",&(*G).kind);
    cout<<"请输入图的顶点数,边数: ";
    cin>>G->vexnum;
    cin>>G->arcnum;

    cout<<"请输入顶点值:"<<endl;
    //构造顶点
    for(i = 0; i < G->vexnum; i++)
    {
        cin>>G->vertices[i].data;
        G->vertices[i].firstarc = NULL;
    }
    if(G->kind == 1 || G->kind == 3)//网
    {
        cout<<"请顺序输入每条弧(边)的权值、弧尾和弧头:\n";
    }else//图
    {
        cout<<"请顺序输入每条弧(边)的弧尾和弧头\n";
    }
    //构造表节点链表
    for(k = 0; k < G->arcnum; k++)
    {
        if(G->kind == 1 || G->kind == 3)//网
        {
            cin>>w;
            cin>>va;
            cin>>vb;
        }else//图
        {
            cin>>va;
            cin>>vb;
        }
        //定位弧尾弧头的位置
        i = LocateVex(*G,va);
        j = LocateVex(*G,vb);

        p = (ArcNode *)malloc(sizeof(ArcNode));
        p->adjvex = j;

        if(G->kind == 1 || G->kind == 3)//网
        {
            p->info = w;//权值
        }else
        {
            p->info = NULL;
        }
        //插入表
        p->nextarc = G->vertices[i].firstarc;//插在表头
        G->vertices[i].firstarc = p;

        //如果是无向图或者无向网,还需要增加对称结点
        if(G->kind == 2 || G->kind == 3)
        {
            p = (ArcNode *)malloc(sizeof(ArcNode));
            p->adjvex = i;

            if(G->kind == 3)//若是无向网,还需要权值
            {
                p->info = w;
            }else
            {
                p->info = NULL;
            }

            //插入表
            p->nextarc = G->vertices[j].firstarc;
            G->vertices[j].firstarc = p;
        }
    }
    return true;
}

void Display(ALGraph G)
{
    ArcNode *p;
    int i;
    switch(G.kind)
    {
    case DG:
        cout<<"有向图";
        break;
    case AG:
        cout<<"无向图";
        break;
    case DN:
        cout<<"有向网";
        break;
    case AN:
        cout<<"无向网";
        break;
    default:
        break;
    }
    cout<<endl;
    cout<<"顶点:"<<endl;
    for(i = 0; i < G.vexnum; i++)
    {
        cout<<G.vertices[i].data<<" ";
    }
    cout<<endl;
    //边
    cout<<"边:"<<endl;
    for(i = 0; i < G.vexnum; i++)
    {
        p = G.vertices[i].firstarc;
        while(p)
        {
            if(G.kind == 0 || G.kind == 1)//有向
            {
                cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data;
                if(G.kind == 1)//有向网
                {
                    cout<<" "<<p->info;
                }

            }else//无向
            {
                if(i < p->adjvex)//不重复打印
                {
                    cout<<G.vertices[i].data<<" "<<G.vertices[p->adjvex].data;
                    if(G.kind == 3)//无向网
                    {
                        cout<<" "<<p->info;
                    }
                }
            }
            cout<<endl;
            p = p->nextarc;
        }
    }

}
void DestroyGraph(ALGraph* G)
{
    ArcNode *p,*q;
    int i;

    for(i = 0; i < G->vexnum; i++)
    {
        p = G->vertices[i].firstarc;
        while(p)
        {
            q = p->nextarc;
            free(p);
            p = q;
        }
    }
    G->arcnum = 0;
    G->vexnum = 0;
}
char GetVex(ALGraph G,int v)
{
    if(v>=G.vexnum || v<0)
    {
        exit(0);
    }
    return G.vertices[v].data;
}
bool PutVex(ALGraph* G,char v,char value)
{
    int i = LocateVex(*G,v);
    if(i == -1)
    {
        return false;
    }
    G->vertices[i].data = value;

    return true;
}
int FirstAdjVex(ALGraph G,char v)
{
    int i = LocateVex(G,v);
    if(i < 0)
    {
        return -1;
    }
    ArcNode *arcNode = G.vertices[i].firstarc;
    if(arcNode == NULL)
    {
        return -1;
    }
    return arcNode->adjvex;
}
int NextAdjVex(ALGraph G,char v,char w)
{
    int i,j;
    i = LocateVex(G,v);
    j = LocateVex(G,w);
    ArcNode *p = G.vertices[i].firstarc;
    while(p && p->adjvex != j)
    {
        p = p->nextarc;
    }
    if(!p || !p->nextarc)//没找到w或w是最后一个邻接点
    {
        return -1;
    }
    else
    {
        return p->nextarc->adjvex;
    }
}
void InsertVex(ALGraph* G,char v)
{
    G->vertices[G->vexnum].data = v;
    G->vertices[G->vexnum].firstarc = NULL;

    G->vexnum++;
}
bool DeleteVex(ALGraph* G,char v)
{
    int i,j;
    ArcNode *p,*q;
    //1.删除邻接表中顶点为v的那一行所有数据,更改弧总数,顶点总数
    i = LocateVex(*G,v);
    if(i < 0 || i >= G->vexnum)//不合法的位置
    {
        return false;
    }
    p = G->vertices[i].firstarc;
    while(p)//依次删除弧
    {
        q = p->nextarc;
        free(p);
        p = q;
        G->arcnum--;
    }
    G->vexnum--;
    //2.更改顶点v之后的顶点在数组中的位置(前移一位)
    for(j = i; j < G->vexnum; j++)
    {
        G->vertices[j] = G->vertices[j+1];
    }
    //3.遍历剩下的邻接表,找到包含顶点v的弧或者边,删除之。另外需要注意,对遍历的每个弧/边,视情况更新序号
    for(j = 0; j < G->vexnum; j++)
    {
        p = G->vertices[j].firstarc;//p指向遍历的顶点的第一条弧或者边
        while(p)
        {
            if(p->adjvex == i)//如果找到指向已删除顶点的弧或者边
            {
                if(p == G->vertices[j].firstarc)//如果待删除的结点是第一个结点
                {
                    G->vertices[j].firstarc = p->nextarc;
                    free(p);
                    p = G->vertices[j].firstarc;
                    if(G->kind <= 1)//如果是有向的,则还需更改弧数
                    {
                        G->arcnum--;
                    }
                }else//不是第一个结点
                {
                    q->nextarc = p->nextarc;
                    free(p);
                    p = q->nextarc;
                    if(G->kind <= 1)//如果是有向的,则还需更改弧数
                    {
                        G->arcnum--;
                    }
                }
            }else//如果当前弧并不是要找的弧,那么继续向后遍历
            {
                if(p->adjvex > i)//(很关键)更新序号
                {
                    p->adjvex--;
                }
                q = p;
                p = p->nextarc;//指向下一条弧
            }
        }
    }
    return true;
}
bool InsertArc(ALGraph* G,char v,char w)
{
    int i,j,weight;
    ArcNode *arcNode;
    //1.得到v、w的在邻接表中的序号
    i = LocateVex(*G,v);
    j = LocateVex(*G,w);
    if(i<0 || j<0)
    {
        return false;
    }
    G->arcnum++;
    if(G->kind == 1 || G->kind == 3)
    {
        cout<<"输入权值:";
        cin>>weight;//输入权值
    }

    //2.生成一个弧结点,插入到顶点v的第一个邻接点的位置(如果是网的话,需要用户输入权值)
    arcNode = (ArcNode*)malloc(sizeof(ArcNode));
    arcNode->adjvex = j;
    if(G->kind == 1 || G->kind == 3)
    {
        arcNode->info = weight;
    }
    else
    {
        arcNode->info = NULL;
    }

    arcNode->nextarc = G->vertices[i].firstarc;
    G->vertices[i].firstarc = arcNode;
    //3.如果是无向的,那么还需生成对称节点,并插到合适位置
    if(G->kind >= 2)
    {
        arcNode = (ArcNode *)malloc(sizeof(ArcNode));
        arcNode->adjvex = i;
        if(G->kind == 3)//无向网
        {
            arcNode->info = weight;
        }
        else
        {
            arcNode->info = NULL;
        }
        arcNode->nextarc = G->vertices[j].firstarc;
        G->vertices[j].firstarc = arcNode;
    }    

    return true;
}
bool DeleteArc(ALGraph* G,char v,char w)
{
    int i,j;
    ArcNode *p,*q;
    //1.得到v、w的在邻接表中的序号
    i = LocateVex(*G,v);
    j = LocateVex(*G,w);
    if(i < 0 || j < 0)
    {
        return false;
    }
    //2.删除v-w
    p = G->vertices[i].firstarc;
    while(p && p->adjvex!=j)
    {
        q = p;
        p = p->nextarc;
    }
    if(p && p->adjvex==j)//找到弧<v-w>
    {
        if(p == G->vertices[i].firstarc)//p指的是第一条弧
        {
            G->vertices[i].firstarc = p->nextarc;
        }
        else
        {
            q->nextarc = p->nextarc;
        }
        free(p);
        G->arcnum--;
    }

    //3.若是无向,则还删除w-v
    if(G->kind >= 2)
    {
        p = G->vertices[j].firstarc;
        while(p && p->adjvex!=i)
        {
            q = p;
            p = p->nextarc;
        }
        if(p && p->adjvex==i)//找到弧<w-v>
        {
            if(p == G->vertices[j].firstarc)//p指的是第一条弧
            {
                G->vertices[j].firstarc = p->nextarc;
            }
            else
            {
                q->nextarc = p->nextarc;
            }
            free(p);
        }
    }
    return true;
}
void DFSTravel(ALGraph* G,void (*Visit)(char))
{
    int i;
    VisitFunc = Visit;
    for(i = 0; i < G->vexnum; i++)
    {
        visited[i] = false;
    }
    for(i = 0; i < G->vexnum; i++)
    {
        if(!visited[i])
        {
            DFS(*G,i);
        }
    }
    cout<<endl;
}
void DFS(ALGraph G,int v)
{
    int i;
    char v1,w1;
    v1 = GetVex(G,v);
    visited[v] = true;
    VisitFunc(G.vertices[v].data);

    for(i = FirstAdjVex(G,v1);i>=0; i = NextAdjVex(G,v1,w1 = GetVex(G,i)))
    {
        if(!visited[i])
        {
            DFS(G,i);
        }
    }
}
void BFSTravel(ALGraph G,void (*Visit)(char))
{
    Queue q;
    InitQueue(&q);
    char w1,u1;
    int i,u,w;
    for(i = 0; i < G.vexnum; i++)
    {
        visited[i] = false;
    }

    for(i = 0; i < G.vexnum; i++)
    {
        if(!visited[i])
        {
            visited[i] = true;
            Visit(G.vertices[i].data);
            EnQueue(&q,i);

            while(!QueueEmpty(q))
            {
                DeQueue(&q,&u);
                u1 = GetVex(G,u);
                for(w = FirstAdjVex(G,u1);w>=0;w = NextAdjVex(G,u1,w1=GetVex(G,w)))
                {
                    if(!visited[w])
                    {
                        visited[w] = true;
                        Visit(G.vertices[w].data);
                        EnQueue(&q,w);
                    }

                }
            }
        }
    }
    DestroyQueue(&q);
    cout<<endl;
}
int main()
{

    ALGraph graph;
    CreateGraph(&graph);
    Display(graph);

    cout<<"深度优先:"<<endl;
    DFSTravel(&graph,Visit);
    cout<<"广度优先:"<<endl;
    BFSTravel(graph,Visit);
    DestroyGraph(&graph);

    return 0;
}

测试:

考虑以下有向图:

图的存储形式——邻接表,布布扣,bubuko.com

时间: 2024-10-18 04:35:50

图的存储形式——邻接表的相关文章

算法导论--图的存储(邻接表与邻接矩阵)

转载请注明出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51888031 图的存储方法有邻接表.邻近矩阵.邻接多重表.十字链表等.本篇文章介绍两种简单且比较常用的两种方法:邻接表与邻接矩阵方法. 以下面的无向图为例,介绍两种存储方法.有向图的存储方法类似,只是边是单方向,无向图的边可以看做双向. 1.邻接链表法 邻接链表表示法对图中的每个顶点建立一个带头的边链表:第i条链表代表依附于顶点vi所有边信息,若为有向图,则表示

图的存储之邻接表

1.稀疏矩阵 有一个稀疏因子,这是节省空间的一种存储方式. 2.邻接表 以邻接矩阵存储图结构的话,当实际边数远远小于图的最大边数时,将会存储很多0,势必造成存储空间的巨大浪费:这时,就必须将邻接矩阵该用为邻接表:将邻接矩阵各行组织为一个单链表,类哈希的存储结构. 存储结构(控制头): int maxVertices;  //最大顶点数 int curVertices;  //当前顶点数 int curEdges;  //当前边数 template<typename Type> class Ed

图的深度优先遍历--邻接表实现

这里用邻接表实现图的深度优先遍历,采用递归实现. #include<iostream> using namespace std; #define VERTEXNUM 5//结点数 struct edgenode { int to; int weight; // 边的权值 edgenode *next; }; struct vnode { int from; edgenode *first; }; void createGraph(vnode *adjilist, int start, int

图的存储形式——邻接矩阵(数组)

邻接矩阵:用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息. 比如考虑下面这个有向图: 如果用邻接矩阵存储可以表示为: 1.顶点数组: 2.邻接矩阵: 图的遍历: 深度优先(DFS): 深度优先搜索遍历类似于树的先根遍历,是树的先根遍历的推广.假设初始状态是图中所有顶点未曾访问过,则深度优先搜索可从图中的某个顶点v出发,访问此顶点,然后依次从v的未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到:若此时图中还有顶点未访问,则另选图中未访问的

图的邻接矩阵与邻接表

一.如何创建邻接表和邻接矩阵? 如图,根据上图建立一个无向图的邻接矩阵和邻接表~ 输入的数据,第一行为两个整数n,e(0<n<=1000,0<e<=5000),表示有n个点和e条边,接下来的e行,每行包含2个整数,表示每条边所连接的两个点. 然后输出图.(无硬性要求) 输入数据: 5 81 01 21 32 32 43 43 04 0 输出数据(示例): 0->1->3->4->NULL1->0->2->3->NULL2->1-

以邻接表作为存储结构的图的深度优先遍历和广度优先遍历(c++版)

一.图的存储 用邻接表法存储图,存储结构分为两部分,一部分为存储图的所有顶点的数组,另一部分为挂载在数组的每个元素后面的用来表示顶点的邻接点的链表. 1.存储顶点的结构单元为: class vnode { public: string nodename; bool visted;//进行图的遍历时用于标记图是否被访问过 node *next; vnode() { visted = false; next = NULL; } }; 链表的结构单元为: class node { public: st

图的存储结构之邻接表(详解)

之前我们介绍过图的邻接矩阵存储法,它的空间和时间复杂度都是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 第一行两个整数n m.n表示顶点个数(顶点编号为1~n),m表示边的条数.接下来m行表示,每行有3个数x y z,表示顶点x到顶点y的边的权值为z.下图就是一种使用链表来实现邻接表的方法. 上面这种实现方法为图中的每一个

图的邻接表表示与无环图的拓扑排序

一.  图的最常用的表示方法是邻接矩阵和邻接表. 1,邻接矩阵 邻接矩阵其实就是一个二维数组,对于每条边<u,v>,我们就令A[u][v] = 1,如果图为有权图,我们也可以令A[u][v]等于该权,这么表示的优点是非常简单,但是它的空间需求很大,如果图是稠密的,邻接矩阵是合适的表示方法,如果图是稀疏的,那这种方法就太浪费空间了,下面给出图的邻接矩阵表示例子. 2 邻接表 邻接表是图的常用储存结构之一.邻接表由表头结点和表结点两部分组成,其中图中每个顶点均对应一个存储在数组中的表头结点.如下图

浅谈数据结构之图的邻接表深度和广度优先遍历(九)

邻接矩阵是一种不错的图存储结构,但是我们发现,对于边数相对较少的图,这种结构是存在对存储空间的极大浪费的.我们知道,顺序存储结构存在预先分配内存可能造成空间浪费的问题,于是引出了链式存储的结构.同样的,我们也可以考虑对边或弧使用链式存储的方式来避免空间浪费的问题.因此,对于图的存储结构,我们同样引入了一种数组与链表相组合的存储方法,我们一般称之为邻接表. 邻接表的处理方法是这样的:(1).图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易的读取顶点的信息,更加方便:另