图论系列:图的表示

一、图的表示

对于一个图(graphG=VE)由顶点集Vvertex)和边集Eedges)组成。每一条边就是一个点对uw),其中uw属于V

1.邻接矩阵(adjacency matrix

    邻接矩阵本质上就是一个二维数组,例如对于每条边(uw),可以表示为A[u][w]=1(边没有权值),否则A[u][w]=0(这条边不存在)。如果边有权值,可以令A[u][w]=权值。

2.邻接表

邻接表使用一个表来存放所有邻接的顶点。

图论系列:图的表示_第1张图片

邻接表:由上图可知,邻接表节点由头结点和表节点组成

 

1)头表结点结构

    ┌────┬─────┐ 

    │data          │ firstedge     

    └────┴─────┘

     顶点vi邻接表的头结点包含两个域:

 ① 顶点域data

  存放顶点vi的信息

 ② 指针域firstedge

  vi的邻接表的头指针。

 

2)边表结点结构

    ┌────┬───┐ 

    │adjvex  next  

    └────┴───┘

     邻接表中每个表结点均有两个域:

 ① 邻接点域adjvex

  存放与vi相邻接的顶点vj的序号j

 ② 链域next

  将邻接表的所有表结点链在一起。

  注意:

    --- 若要表示边上的信息(如权值),则在表结点中还应增加一个数据域。

     ---上述图用邻接表表示为:

    A   B   C   D

A   0   1   1    1

B   1   0   0    1

C   1   0   0    0

D   1   1   0    0

二、C代码实现其数据结构

1.邻接矩阵

在C语言中,如果我们想表示标准C语言不存在的数据类型,一般使用结构体来实现。把已有的数据类型组合成我们想要的数据类型。表示一张图,需要的参数有:图的顶点数,图的边数,一个二维数组,因此可以定义一个结构体包含三种类型的数据

#define ROW 100
#define COL 100
typedef  struct AdjMatrix
{
	int matrix[ROW][COL];
	int NumVertex;      //图的顶点数
	int Numedges;       //图的边数
}MGraph;
//使用指针进行构建,自己管理内存
typedef  struct AdjMatrix_P
{
	int *matrix;
	int NumVertex;  //图的顶点个数
	int Numedges;   //图的边数
}MPGraph;

这里使用了两种方法去构建图的数据类型,第一种方法定义了一个固定长度的二维数组,第二种方法定义一个指向整形类型的指针,但在使用前需要为其分配内存,来存放图的节点和边的信息。

使用第一个数据类型构建一张图:

图论系列:图的表示_第2张图片

void CreateGraph_AM(MGraph *G)
{
	int i=0,j=0;
	int s,t,w;
	cout<<"请输入顶点数和边数:";
	cin>>G->NumVertex>>G->Numedges;
	for(i=0;iNumVertex;i++)
		for(j=0;jNumVertex;j++)
		{
			G->matrix[i][j]=0;
		}

	for(i=0;iNumedges;i++)
	{
		cout<<"请输入第"<>s>>t>>w;
		G->matrix[s][t]=w;
	}
}
void print_AM(MGraph *G)
{
	int i=0,j=0;
	for(i=0;iNumVertex;i++)
	{
		for(j=0;jNumVertex;j++)
		{
			cout<matrix[i][j]<

图的运行效果:

图论系列:图的表示_第3张图片

使用第二种数据类型构建一张图:

void CreateGraph_AMP(MPGraph *G)
{
	int i=0,j=0;
	int s,t,w;
	cout<<"请输入顶点数和边数:";
	cin>>G->NumVertex>>G->Numedges;
	G->matrix=(int *)malloc(G->NumVertex*G->NumVertex*sizeof(int));
	if(G->matrix==NULL)
	{
		cout<<"内存分配失败";
		return;
	}
	for(i=0;iNumVertex;i++)  //初始化
		for(j=0;jNumVertex;j++)
		{
			*(G->matrix+i*G->NumVertex+j)=0;
		}

	for(i=0;iNumedges;i++)
	{
		cout<<"请输入第"<>s>>t>>w;
		*(G->matrix+s*G->NumVertex+t)=w;
	}
}
void print_AMP(MPGraph *G)
{
	int i=0,j=0;
	for(i=0;iNumVertex;i++)
	{
		for(j=0;jNumVertex;j++)
		{
			cout<matrix+i*G->NumVertex+j)<

2.邻接表

邻接表由表头顶点和边表节点组成,因此要先定义头顶点和边表节点的数据结构。

typedef char VertexType;
//边表节点
typedef struct _EdgeNode
{
	int adjvex;              //边表顶点号,用于存放于头表顶点邻接的顶点Vj的序号j
	int weight;              //权值
	struct _EdgeNode * next; //指向下一个边表节点

}EdgeNode;
typedef struct _VNode  //头顶点信息
{
	VertexType data;      //存放顶点的信息。。。
	EdgeNode *firstEdge;
}VNode;

定义好头节点和边节点之后,就可以定义邻接表数据结构了,同邻接矩阵一样,也有2种方法:

#define MAXVERTEXNUM 100
typedef  struct AdjList
{
	VNode adjList[MAXVERTEXNUM];
	int NumVertex;
	int Numedges;
}LGraph;
//用指针来实现	
typedef  struct AdjList_P
{
	VNode *adjList;  //需要分配内存
	int NumVertex;
	int Numedges;
}LPGraph;

使用第一种数据类型构建一张图:

void CreateGraph_AL(LGraph *G)
{
	int i=0;
	int s,t,w;
	VertexType data;
	cout<<"请输入顶点数和边数:";
	cin>>G->NumVertex>>G->Numedges;
	//初始化表头
	for(i=0;iNumVertex;i++)
	{
		cout<<"请输入第"<>data;
		G->adjList[i].data=i;
		G->adjList[i].firstEdge=NULL;
	}
	for(i=0;iNumedges;i++)
	{
		cout<<"请输入第"<>s>>t>>w;
		EdgeNode *p=(EdgeNode*)malloc(sizeof(EdgeNode)); //定义一个边表节点指针,并为其分配内存
		if(p==NULL)
		{
			cout<<"内存分配失败";
			return;
		}
		//修改指针,每次在表头节点后面插入新的节点,其它节点后移
		p->next=G->adjList[s].firstEdge;
		G->adjList[s].firstEdge=p;
		p->adjvex=t;
		p->weight=w;
	}
}
void print_AL(LGraph *G)
{
	int i=0;
	for(i=0;iNumVertex;i++)
	{
		if(G->adjList[i].firstEdge==NULL)
		{
			cout<<"第"<adjList[i].data<<" 邻接表:->NULL";
			cout<adjList[i].data<<" 邻接表:";
			EdgeNode *q=G->adjList[i].firstEdge;
			while(q!=NULL)
			{
				cout<<"->"<adjList[q->adjvex].data;
				q=q->next;
			}
			cout<

图论系列:图的表示_第4张图片

使用第二种数据类型构建一张图:需要为头顶点指针分配内存

void CreateGraph_ALP(LPGraph *G)
{
	int i=0;
	int s,t,w;
	VertexType data;
	cout<<"请输入顶点数和边数:";
	cin>>G->NumVertex>>G->Numedges;
	G->adjList=(VNode *)malloc(G->NumVertex*sizeof(VNode));
	if(G->adjList==NULL)
	{
		printf("内存分配失败");
		return;
	}
	//1.初始化
	for(i=0;iNumVertex;i++)
	{
		cout<<"请输入第"<>data;
		G->adjList[i].data=data;
		G->adjList[i].firstEdge=NULL;
	}
	//2.记录边与权值
	for(i=0;iNumedges;i++)
	{
		cout<<"请输入第"<>s>>t>>w;
		EdgeNode *p=(EdgeNode*)malloc(sizeof(EdgeNode)); //定义一个边表节点指针,并为其分配内存
		if(p==NULL)
		{
			cout<<"内存分配失败";
			return;
		}
		//修改指针,每次在表头节点后面插入新的节点,其它节点后移
		p->next=G->adjList[s].firstEdge;
		G->adjList[s].firstEdge=p;
		p->adjvex=t;
		p->weight=w;
	}
}
void print_ALP(LPGraph *G)
{
	int i=0;
	for(i=0;iNumVertex;i++)
	{
		if(G->adjList[i].firstEdge==NULL)
		{
			cout<<"第"<adjList[i].data<<" 邻接表:->NULL";
			cout<adjList[i].data<<" 邻接表:";
			EdgeNode *q=G->adjList[i].firstEdge;
			while(q!=NULL)
			{
				cout<<"->"<adjList[q->adjvex].data;
				q=q->next;
			}
			cout<


运行效果:

图论系列:图的表示_第5张图片


三、图的两种表示方法比较

(1)找顶点的所有邻接顶点

邻接矩阵:可以判断对应顶点序号的一维数组的值,例如:A[1][...]=? 若=0,邻接;=1邻接

邻接表:扫描对应的邻接表

(2)内存

邻接矩阵占用的内存:O(V平方)

邻接表:O(V+E)

若图是稠密的,选择邻接矩阵是合适的;若图是稀疏的,选择邻接表是合适的;


你可能感兴趣的:(数据结构与算法)