邻接表详解(C/C++)

简介: 目录一、概念二、分类 1)无向图的邻接表2)有向图的邻接表(出弧)3)有向图的逆邻接表(入弧) 三.步骤四、代码

目录


一、概念


二、分类


1)无向图的邻接表


2)有向图的邻接表(出弧)


3)有向图的逆邻接表(入弧)


三.步骤


四、代码


一、概念

邻接表是图的一种链式存储方法,其数据结构包括两部分:节点和邻接点。


二、分类

1)无向图的邻接表

例如,一个无向图及其邻接表如下图所示。一个节点的所有邻接点构成一个单链表

image.png

解释:


• 节点a 的邻接点是节点b 、d ,其邻接点的存储下标为1、3,按照头插法(逆序)将其放入节点a 后面的单链表中;


• 节点b 的邻接点是节点a 、c 、d ,其邻接点的存储下标为0、2、3,将其放入节点b 后面的单链表中;


• 节点c 的邻接点是节点b、d ,其邻接点的存储下标为1、3,将其放入节点c 后面的单链表中;


• 节点d 的邻接点是节点a、b、c ,其邻接点的存储下标为0、1、2,将其放入节点d 后面的单链表中。


无向图邻接表的特点如下。


• 如果无向图有n 个节点、e 条边,则节点表有n 个节点,邻接点表有2e 个节点。


• 节点的度为该节点后面单链表中的节点数。


在上图中,节点数n =4,边数e =5,则在该图的邻接表中,节点表有4个节点,邻接点表有10个节点。节点a 的度为2,其后面单链表中的节点数为2;节点b 的度为3,其后面单链表中的节点数为3。


2)有向图的邻接表(出弧)

例如,一个有向图及其邻接表如下图所示。

image.png

解释:


• 节点a 的邻接点(只看出边,即出弧)是节点b、c、e ,其邻接点的存储下标为1、2、4,按照头插法(逆序)将其放入节点a 后面的单链表中;


• 节点b 的邻接点是节点c ,其邻接点的存储下标为2,将其放入节点b 后面的单链表中;


• 节点c 的邻接点是节点d、e ,其邻接点的存储下标为3、4,按头插法将其放入节点c 后面的单链表中;


• 节点d 的邻接点是节点e ,其邻接点的存储下标为4,将其放入节点d 后面的单链表中;


• 节点e 没有邻接点,其后面的单链表为空。


注意: 对有向图中节点的邻接点,只看该节点的出边(出弧)。


有向图的邻接表的特点如下。


• 如果有向图有n 个节点、e 条边,则节点表有n 个节点,邻接点表有e 个节点。


• 节点的出度为该节点后面单链表中的节点数。


在上图中,节点数n =5,边数e =7,则在该图的邻接表中,节点表有5个节点,邻接点表有7个节点。节点a 的出度为3,其后面单链表中的节点数为3;节点c 的出度为2,其后面单链表中的节点数为2。


在有向图邻接表中很容易找到节点的出度,但是找入度很难,需要遍历所有邻接点表中的节点,查找到该节点出现了多少次,入度就是多少。例如在下图中,节点c 的下标为2,在邻接点表中有两个为2的节点,因此节点c 的入度为2;节点e 的下标为4,在邻接点表中有3个为4的节点,因此节点e 的入度为3。


image.png

3)有向图的逆邻接表(入弧)

有时为了方便得到节点的入度,可以建立一个有向图的逆邻接表,如下图所示。

image.png

解释:


• 节点a 没有逆邻接点(只看入边,即入弧),其后面的单链表为空;


• 节点b 的逆邻接点是节点a ,其邻接点的存储下标为0,将其放入节点b 后面的单链表中;


• 节点c 的逆邻接点是a、b ,其邻接点的存储下标为0、1,按照头插法将其放入节点c 后面的单链表中;


• 节点d 的逆邻接点是节点c ,其邻接点的存储下标为2,将其放入节点d 后面的单链表中;


• 节点e 的逆邻接点是节点a、c、d ,其邻接点的存储下标为0、2、3,按照头插法(逆序)将其放入节点e 后面的单链表中。


注意: 对有向图中节点的逆邻接点,只看该节点的入边(入弧)。


有向图的逆邻接表的特点如下。


(1)如果有向图有n 个节点、e 条边,则节点表有n 个节点,邻接点表有e 个节点。


(2)节点的入度为该节点后面的单链表中的节点数。


在上图中,节点数n =5,边数e =7,在该图的邻接表中,节点表有5个节点,邻接点表有7个节点。节点a 的入度为其后面的单链表中的节点数0,节点c 的入度为其后面的单链表中的节点数2。


三.步骤


算法步骤:


(1)输入节点数和边数;


(2)依次输入节点信息,将其存储到节点数组Vex[]的data域中,将Vex[]的first域置空;


(3)依次输入每条边依附的两个节点,如果是网,则还需要输入该边的权值。


• 如果是无向图,则输入a b ,查询节点a、b 在节点数组Vex[]中的存储下标i 、j ,创建一个新的邻接点s ,令s ->v =j ; s ->next=NULL;然后将节点s 插入第i 个节点的第1个邻接点之前(头插法)。在无向图中,从节点a 到节点b 有边,从节点b 到节点a 也有边,因此还需要创建一个新的邻接点s 2 ,令s 2 ->v =i ; s 2 ->next=NULL;然后将s 2 节点插入第j 个节点的第1个邻接点之前(头插法)。


• 如果是有向图,则输入a b ,查询节点a、b 在节点数组Vex[]中的存储下标i 、j ,创建一个新的邻接点s ,令s ->v =j ; s ->next=NULL;将节点s 插入第i 个节点的第1个邻接点之前(头插法)。


• 如果是无向网或有向网,则和无向图或有向图的处理方式一样,只是邻接点多了一个权值域。


图解: 一个有向图如下图所示,其邻接表的存储过程如下所述。

image.png

(1)输入节点数5和边数7,G .vexnum=5,G .edgenum=7。

(2)输入节点信息a b c d e 并将其存入节点表,存储结果如下图所示。

image.png

(3)依次输入每条边依附的两个节点。

• 输入ab ,处理结果:在Vex[]数组的data域中查找到节点ab 的下标分别为0、1,创建一个新的邻接点s ,令s ->v =1; s ->next=NULL。将节点s 插入第0个节点的第1个邻接点之前(头插法)。

image.png

• 输入a c ,处理结果:在Vex[]数组的data域中查找到节点ac 的下标分别为0、2,创建一个新的邻接点s ,令s ->v=2; s ->next=NULL。将节点s 插入第0个节点的第1个邻接点之前(头插法)。

image.png

• 输入a e ,处理结果:在Vex[]数组的data域中查找到节点ae 的下标分别为0、4,创建一个新的邻接点s ,令s ->v =4; s ->next=NULL。将节点s 插入第0个节点的第1个邻接点之前(头插法)。

image.png

 • 输入b c ,处理结果:在Vex[]数组的data域中查找到节点bc 的下标分别为1、2,创建一个新的邻接点s ,令s ->v =2; s ->next=NULL。将节点s 插入第1个节点的第1个邻接点之前(头插法)。

image.png

• 输入c d ,处理结果:在Vex[]数组的data域中查找到节点cd 的下标分别为2、3,创建一个新的邻接点s ,令s ->v =3; s ->next=NULL。将节点s 插入第2个节点的第1个邻接点之前(头插法)。

image.png

• 输入ce ,处理结果:在Vex[]数组的data域中查找到ce 的下标分别为2、4,创建一个新的邻接点s ,令s ->v =4; s ->next=NULL。将节点s 插入第2个节点的第1个邻接点之前(头插法)。

image.png

• 输入d e ,处理结果:在Vex[]数组的data域中查找到节点de 的下标分别为3、4,创建一个新的邻接点s ,令s ->v =4; s ->next=NULL。将节点s 插入第3个节点的第1个邻接点之前(头插法)。

image.png

注意: 由于后输入的内容被插在了单链表的前面,因此若输入顺序不同,则建立的单链表也不同。

四、代码

代码如下

//创建有向图的邻接表
#include <iostream>
using namespace std;
const int MaxVnum=100;//顶点数最大值
typedef char VexType;//顶点的数据类型为字符型
typedef struct AdjNode{ //定义邻接点类型
  int v; //邻接点下标
  struct AdjNode *next; //指向下一个邻接点
}AdjNode;
typedef struct VexNode{ //定义顶点类型
  VexType data; // VexType为顶点的数据类型,根据需要定义
  AdjNode *first; //指向第一个邻接点
}VexNode;
typedef struct{//定义邻接表类型
    VexNode  Vex[MaxVnum];
    int vexnum,edgenum; //顶点数,边数
}ALGragh;
int locatevex(ALGragh G,VexType x)
{
    for(int i=0;i<G.vexnum;i++)//查找顶点信息的下标
       if(x==G.Vex[i].data)
        return i;
    return -1;//没找到
}
void insertedge(ALGragh &G,int i,int j)//插入一条边
{
    AdjNode *s;
    s=new AdjNode;
    s->v=j;
    s->next=G.Vex[i].first;
    G.Vex[i].first=s;
}
void printg(ALGragh G)//输出邻接表
{
   cout<<"----------邻接表如下:----------"<<endl;
   for(int i=0;i<G.vexnum;i++)
   {
       AdjNode *t=G.Vex[i].first;
       cout<<cout<<G.Vex[i].data<<":  ";
       while(t!=NULL)
       {
           cout<<"["<<t->v<<"]  ";
           t=t->next;
       }
       cout<<endl;
   }
}
void CreateALGraph(ALGragh &G)//创建有向图邻接表
{
    int i,j;
    VexType u,v;
    cout<<"请输入顶点数和边数:"<<endl;
    cin>>G.vexnum>>G.edgenum;
    cout << "请输入顶点信息:"<<endl;
    for(i=0;i<G.vexnum;i++)//输入顶点信息,存入顶点信息数组
        cin>>G.Vex[i].data;
    for(i=0;i<G.vexnum;i++)
        G.Vex[i].first=NULL;
    cout<<"请依次输入每条边的两个顶点u,v"<<endl;
    while(G.edgenum--)
    {
        cin>>u>>v;
        i=locatevex(G,u);//查找顶点u的存储下标
        j=locatevex(G,v);//查找顶点v的存储下标
        if(i!=-1&&j!=-1)
            insertedge(G,i,j);
        else
        {
           cout << "输入顶点信息错!请重新输入!"<<endl;
           G.edgenum++;//本次输入不算
        }
    }
}
int main()
{
    ALGragh G;
    CreateALGraph(G);//创建有向图邻接表
    printg(G);//输出邻接表
    return 0;
}
相关文章
|
7月前
|
存储
邻接表详解
邻接表详解
46 0
|
7月前
|
存储 算法
有向图和无向图的表示方式(邻接矩阵,邻接表)
有向图和无向图的表示方式(邻接矩阵,邻接表)
415 0
|
存储 机器学习/深度学习 人工智能
图的存储及基本操作总结(邻接矩阵、邻接表)及C/C++代码实现
图的存储及基本操作总结(邻接矩阵、邻接表)及C/C++代码实现
1459 1
|
存储
图操作之邻接矩阵与邻接表的深度优先遍历
图操作之邻接矩阵与邻接表的深度优先遍历
212 0
拓扑排序(邻接表实现)
拓扑排序(邻接表实现)
202 0
拓扑排序(邻接表实现)
|
机器学习/深度学习
有向图,无向图的邻接矩阵和邻接表模板
有向图,无向图的邻接矩阵和邻接表模板
204 0
有向图,无向图的邻接矩阵和邻接表模板
|
算法
最小生成树:Kruskal算法(邻接表+最小堆+并查集)
最小生成树:Kruskal算法(邻接表+最小堆+并查集)
277 0
最小生成树:Kruskal算法(邻接表+最小堆+并查集)
邻接矩阵
数据结构中无向图邻接矩阵的存储
邻接矩阵