数据结构之图的邻接表

1.邻接表的简介:

图的邻接矩阵存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。邻接表由表头结点和表结点两部分组成,其中图中每个顶点均对应一个存储在数组中的表头结点。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。如词条概念图所示,表结点存放的是邻接顶点在数组中的索引。对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点。[1]

邻接表是图的一种最主要存储结构,用来描述图上的每一个点。对图的每个顶点建立一个容器(n个顶点建立n个容器),第i个容器中的结点包含顶点Vi的所有邻接顶点。实际上我们常用的邻接矩阵就是一种未离散化每个点的边集的邻接表。

有向图中,描述每个点向别的节点连的边(点a->点b这种情况)。

在无向图中,描述每个点所有的边(点a-点b这种情况)

与邻接表相对应的存图方式叫做边集表,这种方法用一个容器存储所有的边。

工业上有很多非常好的图库的实现,例如C++的boost graph库.如果可以,尽量用这些库,这样可以大大提高你的效率

2.表示法

注意:

n个顶点e条边的无向图的邻接表表示中有n个顶点表结点和2e个边结点。(换句话说,每条边(i,j)在邻接表 中出现两次:一次在关于i的邻接表中,另一次在关于j的邻接表中)

有向图

对于有向图,vi的邻接表中每个表结点都对应于以vi为始点射出的一条边。因此,将有向图的邻接表称为出边表。

【例】有向图G6如下图所示,其中顶点v1的邻接表上两个表结点中的顶点序号分别为0和4,它们分别表示从v1射出的两条边(简称为v1的出边):<v1,v0>和<v1,v4>。

注意:

n个顶点e条边的有向图,它的邻接表表示中有n个顶点表结点和e个边表结点。(因为有向图是单向的)

逆邻接表

在有向图中,为图中每个顶点vi建立一个入边表的方法称逆邻接表表示法。

入边表中的每个表结点均对应一条以vi为终点(即射入vi)的边。

【例】G6的逆邻表如上面(b)图所示,其中v0的入边表上两个表结点1和3分别表示射人v0的两条边(简称为v0的入边):<v1,v0>和<v3,v0>。

注意:

n个顶点e条边的有向图,它的逆邻接表表示中有n个顶点表结点和e个边表结点。

3.邻接表的形式说明。

邻接表是一个二维容器,第一维描述某个点,第二维描述这个点所对应的边集们。

实现邻接表的方法绝对有100种以上。即使是前向星这种东西也是邻接表,因为它还是描述某个点和这个点所对应的边集们.

我们说说常用的邻接表存图法(静态的array就不说了.)必须有开O1以及以上编译的条件,不然没有测试的效率无任何意义。

第一维是描述点的。可以用vector,list,forward_list,deque,map,multimap,unordered_map,unordered_multimap等(一般不能用set,mutiset,unordered_set,unordered_multiset).

按照你的要求去选择。一般来讲存完图以后不涉及点的加入与删除优先使用vector.map,multimap,unordered_map,unordered_multimap.

第二维是描述这个点的边集,可以用全部的容器。也是的一般来讲存完图以后,不涉及点的加入与删除优先使用vector,空间充足可以考虑deque.涉及点的删除用forward_list或者是list,map,multimap,unordered_map,unordered_multimap.

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <queue>
using namespace std;
#define N 200
typedef enum GraphKind {DG,DN,UDG,UDN};
typedef char VertexType[20];
typedef struct ArcNode
{
    int adjvex;//该弧指向的顶点的位置
    int *info;   //该弧的相关信息
    ArcNode *nextarc;//指向下一条弧的指针
};
typedef struct
{
    VertexType data;//顶点信息
    ArcNode *firstarc; //指向第一条依附该顶点的弧的指针
} VNode,AdjList[N];
typedef struct ALGraph
{
    AdjList vertices;
    int vexnum,arcnum;//弧的当前顶点数和弧数
    int kind;//图的种类
};
//1.顶点的位置:
int LocateVex(ALGraph g,VertexType u)
{
    int i;
    for(i=0; i<g.vexnum; i++)
    {
        if(strcmp(g.vertices[i].data,u)==0) return i;
    }
    return -1;
}
//2.采用邻接表存储结构,构造没有相关信息的图G
//(用一个函数构造4种图)
void CreateGraph(ALGraph &G)
{
    int i, j, k;
    int w;      //权值
    VertexType va , vb ;
    ArcNode *p;
    cout << "请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3):" << endl;
    cin >> G.kind;
    cout << "请输入图的顶点数,边数: " << endl;
    cin >> G.vexnum >> G.arcnum;
    cout  << "请输入" << G.vexnum << "个顶点的值(" << "1" << "个字符)每个用空格隔开:" << 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 << "请顺序输入每条弧(边)的权值,弧尾和弧头(以空格作为间隔):" << endl;
    else                                //图
        cout << "请顺序输入每条弧边的弧尾和弧头(以空格作为间隔):" << endl;
    for( k = 0; k < G.arcnum; k++ )     //构造表结点链表
    {
        if( G.kind == 1 || G.kind == 3 )  //网
            cin >> w >> va >> vb;
        else                             //图
            cin >> va >> 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 = ( int * )malloc( sizeof( int ) );
            *( p->info ) = w;
        }
        else
            p->info = NULL;
        p->nextarc = G.vertices[i].firstarc;  //插在表头
        G.vertices[i].firstarc = p;
        if( G.kind >= 2 )                        //无向图或网,产生第二个表结点
        {
            p = ( ArcNode* )malloc( sizeof( ArcNode ) );
            p->adjvex = i;
            if( G.kind == 3 )               //无向图
            {
                p->info = ( int * )malloc( sizeof( int ) );
                *( p->info ) = w;
            }
            else
                p->info = NULL;            //无向图
            p->nextarc = G.vertices[j].firstarc;  //插在表头
            G.vertices[j].firstarc = p;
        }
    }
}
void Prt(ALGraph G)
{
    int i;
    printf("vexnum :%d, arcnum :%d.\n\n",G.vexnum,G.arcnum);
    printf("VexList:\n");
    for(i = 0; i < G.vexnum; i++){
        printf("        v(%d):%s|",i,G.vertices[i].data);
        ArcNode *p = G.vertices[i].firstarc;
        while(p){
            printf("->%d",p->adjvex);
            p = p->nextarc;
        }
        printf("\n");
    }
}
void find(ALGraph &g,VertexType a,VertexType b)
{
    int i,j;
    i=LocateVex(g,a);
    j=LocateVex(g,b);
    ArcNode *p,*q1,*q2;
    if(g.kind>=2)
    {
        p=g.vertices[i].firstarc;
        int flag=0;
        while(p)
        {
            if(p->adjvex==j)
            {
                flag=1;
                break;
            }
            p=p->nextarc;
        }
        if(flag==1)
        {
            cout<<"YES"<<endl;
            if(g.kind%2==1) cout<<*(p->info)<<endl;
        }
        else
        {
            cout<<"NO"<<endl;
        }
    }
    else
    {
        q1=g.vertices[i].firstarc;
        int flag1=0,flag2=0;
        while(q1)
        {
            if(q1->adjvex==j)
            {
                flag1=1;
                break;
            }
            q1=q1->nextarc;
        }
        q2=g.vertices[j].firstarc;
        while(q2)
        {

            if(q2->adjvex==i)
            {
                flag2=1;
                break;
            }
            q2=q2->nextarc;
        }
        if(flag1==1)
        {
            cout<<a<<"->"<<b<<endl;
            if(g.kind%2==1) cout<<*(q1->info)<<endl;
        }
        else if(flag2==1)
        {
            cout<<b<<"->"<<a<<endl;
            if(g.kind%2==1) cout<<*(q2->info)<<endl;
        }
        else
        {
            cout<<"NO"<<endl;
        }
    }
}
void degree(ALGraph &g,VertexType a)
{
    int i,j,k;
    ArcNode *p,*q;
    if(g.kind>1)
    {
        int sum=0;
        i=LocateVex(g,a);
        p=g.vertices[i].firstarc;
        while(p)
        {
            sum++;
            p=p->nextarc;
        }
        cout<<a<<"的度为:"<<sum<<endl;
    }
    else
    {
        int sum1=0,sum2=0;
        i=LocateVex(g,a);
        p=g.vertices[i].firstarc;
        while(p)
        {
            sum1++;
            p=p->nextarc;
        }
        for(j=0; j<g.vexnum; j++)
        {
            if(j!=i)
            {
                q=g.vertices[j].firstarc;
                while(q)
                {
                    if(q->adjvex==i)
                    {
                        sum2++;
                    }
                    q=q->nextarc;
                }
            }
        }
        cout<<a<<"的度为:"<<(sum1+sum2)<<endl;
    }
}
bool vis[N];
//返回v的第一个邻接顶点的序号,若图中无邻接顶点,返回-1
int FirstAdjVex(ALGraph G,VertexType v){
    int i = LocateVex(G,v);
    ArcNode *p = G.vertices[i].firstarc;
    if(p){
        return p->adjvex;
    }
    else{
        return -1;
    }
}
//返回v的(相对于w的)下一个邻接顶点的序号。若w是v的最后一个邻接顶点,则返回-1
int NextAdjVex(ALGraph G,VertexType v,VertexType w){
    int i = LocateVex(G,v);
    int j = LocateVex(G,w);
    ArcNode *p = G.vertices[i].firstarc;
    while(p){
        if(p->adjvex == j) break;
        p = p->nextarc;
    }
    if(!p || !p->nextarc){ //没找到w或w是最后一个邻接点
                          return -1;
                         }
    else{
        return (p->nextarc)->adjvex;
    }
}
//深度优先搜索
void DFS(ALGraph G,int v){
    int w;
    vis[v] = true;
    printf("%s ",G.vertices[v].data); //访问第v个顶点

    w = FirstAdjVex(G,G.vertices[v].data);
    while(w >= 0){
        if(!vis[w]){
            DFS(G,w);
        }
        w = NextAdjVex(G,G.vertices[v].data,G.vertices[w].data);
    }
}
//对图做深度优先遍历
void DFSTraverse(ALGraph G){
    int v;
    memset(vis,false,sizeof(vis));
    for(v = 0; v < G.vexnum; v++){
        if(!vis[v]){
            DFS(G,v);//对尚未访问的顶点调用DFS
        }
    }
    printf("\n");
}
//对图做广度优先搜索
void BFSTraverse(ALGraph G)
{
    int v,u,w;
    queue<int>q;
    memset(vis,false,sizeof(vis));
    for(v = 0; v < G.vexnum; v++){
        //如果是连通图,v = 0就遍历全图
        if(!vis[v]){
            vis[v] = true;
            printf("%s ",G.vertices[v].data);
        }
        q.push(v);
        while(!q.empty()){
            u=q.front();
            q.pop(); //队头元素出队,并置为u
            w = FirstAdjVex(G,G.vertices[u].data);
            while(w >= 0){
                if(!vis[w]){
                    vis[w] = true;
                    printf("%s ",G.vertices[w].data);
                   q.push(w);//入队
                }
                w = NextAdjVex(G,G.vertices[u].data,G.vertices[w].data);
            }
        }
    }
    printf("\n");
}
int main()
{
    ALGraph g;
    CreateGraph(g);
    printf("输出邻接表:\n");
    Prt(g);
    VertexType a,b;
    printf("输入两个顶点:\n");
    cin>>a>>b;
    printf("输出顶点之间是否存在边:\n");
    find(g,a,b);
    printf("输入一个顶点:\n");
    cin>>a;
    printf("输出该点的度:\n");
    degree(g,a);
    printf("深度优先搜索遍历:\n");
    DFSTraverse(g);
    printf("广度优先搜索遍历:\n");
    BFSTraverse(g);
    return 0;
}
时间: 2024-08-04 17:01:30

数据结构之图的邻接表的相关文章

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

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

数据结构(10) -- 图的邻接表存储

////////////////////////////////////////////////////////// //图的邻接表存储 ////////////////////////////////////////////////////////// #include <iostream> #include <stdlib.h> using namespace std; //图的邻接表表示法 #define MaxVertexNum 100 enum GraphType{DG,

数据结构学习笔记05图 (邻接矩阵 邻接表--&gt;BFS DFS)

数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边<v, w> 表示从v指向w的边(单行线) 不考虑重边和自回路 无向图:边是无向边(v, w) 有向图:边是有向边<v, w> 连通:如果从V到W存在一条(无向)路径,则称V和W是连通的 连通图(Connected Graph):如果对于图的任一两个顶点v.w∈V,v和w都是连通的,则称

数据结构之---C语言实现图的邻接表存储表示

// 图的数组(邻接矩阵)存储表示 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME 3 // 顶点字符串的最大长度+1 #define MAX_VERTEX_NUM 20 typedef int InfoType; // 存放网的权值 typedef char VertexType[MAX_NAME]; // 字符串类型 typedef enum{DG, DN, AG

看数据结构写代码(36) 图的邻接表表示与实现

图的邻接表表示法,是为每一个顶点建立一个链表,链表里存放着相同弧尾的 弧的信息,这些链表顺序存放在数组中.下面是无向图g2的邻接表 邻接表 比 邻接矩阵 节省空间,同时 也带来一些操作上的 不便,例如 看 两个顶点是否 相邻,需要 遍历 链表,在 求 无向图顶点的度时,只需 遍历 顶点的链表,而 求 有向图 顶点的度 需要 遍历 整个图 查找 弧头 为这个顶点的 个数. 如果 不想这样做,可以 建立 逆邻接表,即 链表里 存放着 相同 弧头的 弧 的信息. 下一节 要说的 十字链表 类似于这种结

图的邻接表表示、广度优先、深度优先搜索

图,就是我们在数据结构中学到的图,它是一种存储信息的结构.图是一类在实际应用中非常常见的数据结构,当数据规模大到一定程度时,如何对其进行高效计算即成为迫切需要解决的问题.最常见的大规模图数据的例子就是互联网网页数据,网页之间通过链接指向形成规模超过500 亿节点的巨型网页图.再如,Facebook 社交网络也是规模巨大的图,仅好友关系已经形成超过10 亿节点.千亿边的巨型图,考虑到Facebook 正在将所有的实体数据节点都构建成网状结构,其最终形成的巨型网络数据规模可以想见其规模.要处理如此规

图(邻接表)

我们先来看一个图 我们想将这个图的信息存储到邻接表中,我们需要一个数组保存节点信息,还要有一个节点用来保存与该节点相邻的节点信息. 1 typedef struct arc_node 2 { 3 int pos; 4 int distance; 5 struct arc_node * next; 6 } Arc_node;//保存Node节点的相邻节点信息 7 8 typedef struct node 9 { 10 node_type info; 11 Arc_node * next; 12

_DataStructure_C_Impl:图的邻接表存储

#include<stdio.h> #include<stdlib.h> #include<string.h> //图的邻接表类型定义 typedef char VertexType[4]; typedef char InfoPtr; typedef int VRType; #define INFINITY 10000 //定义一个无限大的值 #define MaxSize 50 //最大顶点个数 typedef enum{DG,DN,UG,UN}GraphKind;

基于C++ STL图的邻接表表示及深度、广度搜索实现

基于C++ STL图的邻接表表示及深度.广度搜索实现,对图论的学习有帮助,代码如下: #include <iostream> #include <vector> #include <set> using namespace std; #define MAX(a, b) ((a) > (b) ? (a) : (b) ) //定义图的定点 typedef struct Vertex { int id; vector<int> connectors; //存