图论系列:图的表示

一、图的表示

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

1.邻接矩阵(adjacency matrix

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

2.邻接表

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


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

 

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;

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

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


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

	for(i=0;i<G->Numedges;i++)
	{
		cout<<"请输入第"<<i+1<<"边的起点,终点和权值:";
		cin>>s>>t>>w;
		G->matrix[s][t]=w;
	}
}
void print_AM(MGraph *G)
{
	int i=0,j=0;
	for(i=0;i<G->NumVertex;i++)
	{
		for(j=0;j<G->NumVertex;j++)
		{
			cout<<left;    //左对齐,占五个字符
			cout<<setw(5)<<G->matrix[i][j]<<setw(5);
		}
		cout<<endl;
	}
}

图的运行效果:


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

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;i<G->NumVertex;i++)  //初始化
		for(j=0;j<G->NumVertex;j++)
		{
			*(G->matrix+i*G->NumVertex+j)=0;
		}

	for(i=0;i<G->Numedges;i++)
	{
		cout<<"请输入第"<<i+1<<"边的起点,终点和权值:";
		cin>>s>>t>>w;
		*(G->matrix+s*G->NumVertex+t)=w;
	}
}
void print_AMP(MPGraph *G)
{
	int i=0,j=0;
	for(i=0;i<G->NumVertex;i++)
	{
		for(j=0;j<G->NumVertex;j++)
		{
			cout<<left;    //左对齐,占五个字符
			cout<<setw(5)<<*(G->matrix+i*G->NumVertex+j)<<setw(5);
		}
		cout<<endl;
	}
}

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;i<G->NumVertex;i++)
	{
		cout<<"请输入第"<<i+1<<"顶点的信息:";
		cin>>data;
		G->adjList[i].data=i;
		G->adjList[i].firstEdge=NULL;
	}
	for(i=0;i<G->Numedges;i++)
	{
		cout<<"请输入第"<<i+1<<"边的起点,终点和权值:";
		cin>>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;i<G->NumVertex;i++)
	{
		if(G->adjList[i].firstEdge==NULL)
		{
			cout<<"第"<<i+1<<"顶点:"<<G->adjList[i].data<<" 邻接表:->NULL";
			cout<<endl;
		}
		else
		{
			cout<<"第"<<i+1<<"顶点:"<<G->adjList[i].data<<" 邻接表:";
			EdgeNode *q=G->adjList[i].firstEdge;
			while(q!=NULL)
			{
				cout<<"->"<<G->adjList[q->adjvex].data;
				q=q->next;
			}
			cout<<endl;
		}
	}
}


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

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;i<G->NumVertex;i++)
	{
		cout<<"请输入第"<<i+1<<"顶点的信息:";
		cin>>data;
		G->adjList[i].data=data;
		G->adjList[i].firstEdge=NULL;
	}
	//2.记录边与权值
	for(i=0;i<G->Numedges;i++)
	{
		cout<<"请输入第"<<i+1<<"边的起点,终点和权值:";
		cin>>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;i<G->NumVertex;i++)
	{
		if(G->adjList[i].firstEdge==NULL)
		{
			cout<<"第"<<i+1<<"顶点:"<<G->adjList[i].data<<" 邻接表:->NULL";
			cout<<endl;
		}
		else
		{
			cout<<"第"<<i+1<<"顶点:"<<G->adjList[i].data<<" 邻接表:";
			EdgeNode *q=G->adjList[i].firstEdge;
			while(q!=NULL)
			{
				cout<<"->"<<G->adjList[q->adjvex].data;
				q=q->next;
			}
			cout<<endl;
		}
	}
}


运行效果:



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

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

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

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

(2)内存

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

邻接表:O(V+E)

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


  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值