图的存储结构的实现(C语言 邻接表/邻接矩阵)

目录

图的存储结构

完整代码:

因图中可能存在回路,某些顶点可能会被重复访问,那么如何避免遍历不会因回路而陷入死循环。

邻接矩阵实现图的存储结构

是否可以采用顺序存储结构存储图?

如何存储图?

邻接矩阵数组表示

​编辑

全局定义结构体

邻接矩阵创建图

邻接矩阵输出图

基于邻接矩阵深度优先遍历

基于邻接矩阵的广度优先遍历

如何求顶点 i 的所有邻接点?

有向图的邻接矩阵一定不对称吗?

如何判断从顶点 i 到顶点 j 是否存在边?

邻接表实现图的存储结构

图的邻接矩阵存储结构的空间复杂度?

如果为稀疏图则会出现什么现象?

边表中的结点表示什么?

如何求顶点 i 的度?

如何判断顶点 i 和顶点 j之间是否存在边?

如何求顶点 i 的出度?

如何求顶点 i 的入度?

如何求顶点 i 的所有邻接点?

​编辑

定义邻接表结点

创建邻接表

输出邻接表

基于邻接表的深度优先遍历

基于邻接表的广度优先遍历

主函数


图的存储结构

完整代码:

#include"stdio.h"
#include"stdlib.h"
typedef int datatype;
#define N 10
int vertexnum=4,vertexarc=4;
int i,j;
//用于存储图的邻接矩阵的数组
struct node
{ 
	datatype vertex[N];
	int arc[N][N];
	int degree_out[N];  //顶点的出度 
	int degree_in[N];   //顶点的入度  
}mGraph;
//用于存储图的邻接表的顶点结点和边表结点 
typedef struct ArcNode
{    int adjvex;  
     struct ArcNode *next;
}Arcnode;
struct VertexNode 
{
      datatype  vertex;
      Arcnode *firstedge;
      int degree_out;   //顶点的出度 
      int degree_in;    //顶点的入度 
} adjlist[N];

int visited[N];

//初始化visited数组
void initVisited(int vertexNum)
{
	int i;
	for (i=0; i<vertexNum; i++) 
		visited[i] = 0;
} 
int creatGraph(int vertexnum,int vertexarc)//创建邻接矩阵
{
	int l,k,j,i;
	printf("请输入四个顶点的值\n");
	for(int i=0;i<vertexnum;i++)
	scanf("%d",&mGraph.vertex[i]);
	for(int i=0;i<vertexnum;i++)
	for(int j=0;j<vertexnum;j++)
	mGraph.arc[i][j]=0;
	printf("请输入每条边的两个顶点\n");
	for(i=0;i<vertexarc;i++)
	{
		scanf("%d%d",&l,&k);
		l--;k--;
		mGraph.arc[l][k]=1;
		mGraph.degree_out[l]++;
		mGraph.degree_in[k]++;		
	}	 
} 
int ALgraph(int vertexnum,int vertexarc)//邻接表的创建
 {
 	Arcnode *p;
 	char a[vertexnum];
 	puts("请输入四个顶点的值");
	for(i=0;i<vertexnum;i++)
	{
		scanf("%d",&adjlist[i].vertex);
		adjlist[i].firstedge=NULL;
	}	
	puts("输入每条边的两个顶点");
	int k,l;//用于接收边的两个顶点 
	for(int j=0;j<vertexarc;j++)
	{	
		scanf("%d%d",&k,&l);
		k--;l--;
		adjlist[k].degree_out++;
		adjlist[l].degree_in++;
		p=(Arcnode *)malloc(sizeof(Arcnode));
		p->adjvex=l;
		p->next=adjlist[k].firstedge;
		adjlist[k].firstedge=p;
	}
 } 
void display(int vertexnum,int vertexarc)//输出图的邻接矩阵 
{
	int i,j;
	puts("输出顶点的值");
	for(i=0;i<vertexnum;i++)
	printf("%d  ",mGraph.vertex[i]);
	putchar('\n');
	puts("输出邻接矩阵"); 
	for(i=0;i<vertexnum;i++)
	{for(j=0;j<vertexnum;j++)
		printf("%d  ",mGraph.arc[i][j]);
		putchar('\n');
	}
}
void displayAlgraph(int vertexnum)//邻接表的输出 
{
	/*
	puts("输出顶点的值为");
	for(i=0;i<vertexnum;i++)
	{
		printf("%d ",adjlist[i].vertex);
	}*/
	
	/*
puts("输出顶点以及邻接边");
	for(i=0;i<vertexnum;i++)
	{
			printf("%d ",adjlist[i].vertex);
			printf(":");
			printf("(%d,%d) ",adjlist[i].vertex,)
	} */
	ArcNode *p;
	for(i=0;i<vertexnum;i++)
	{
		p=adjlist[i].firstedge;
		printf("%d :",i+1);
		while(p!=NULL)
		{
			//printf("%d ",p->adjvex);
			printf("(%d,%d)  ",adjlist[i].vertex,adjlist[p->adjvex].vertex);
			p=p->next;
		}
		printf("\n");
	}
} 
void tongji(int vertexnum)//邻接矩阵统计出度和入度 
{
	for(int i=0;i<vertexnum;i++)
	printf("顶点 %d 的出度为:%d   入度为%d\n",mGraph.vertex[i],mGraph.degree_out[i],mGraph.degree_in[i]);
}
void tongji2(int vertexnum)//邻接表统计出度和入度 
{
	for(i=0;i<vertexnum;i++)
	{
		printf("顶点%d 的出度为:%d 入度为: %d\n",adjlist[i].vertex,adjlist[i].degree_out,adjlist[i].degree_in);
	}	
}
int  DFSTraversel(int v)//基于邻接矩阵的深度优先遍历  
{
     printf("%d ",mGraph.vertex[v]); 
     visited [v]=1;
     for (int j=0; j<vertexnum; j++)
         if (mGraph.arc[v][j]==1 && visited[j]==0)
            DFSTraversel( j );
}
int DFSTraverse(int v)//基于邻接表的深度优先遍历
{
	Arcnode *p;
	p=adjlist[v].firstedge;
	printf("%d ",adjlist[v].vertex);
	visited[v]=1;
	while(p)
	{
		j=p->adjvex;
		if(visited[j]==0)
		DFSTraverse(j);
		p=p->next;
	}
	
} 
/*
void enquene(linklist *rear,int v)
{
	linklist *T;
	T=(linklist *)malloc(sizeof(linklist));
	T->data=v;
	T->next=NULL;
	rear->next=T;
	rear=T;	
}*/
int DFSTraversel2(int v)//基于邻接矩阵的广度优先遍历
{
	printf("基于邻接矩阵的广度优先遍历"); 
	/*
Arcnode *p;
	
	linklist *L;
	L=(linklist *)malloc(sizeof(linklist));
	L->front=L->rear=0;
	linkqene *rear,*front;
	front=(linkqene *)malloc(sizeof(linkqene));
	front->next=NULL;
	rear=front;
	
	printf("%d",mGraph.vertex[v]);
	visited[v]=1;
	enquene(rear,v);//入队 */
	int rear=-1,front=-1;
	int Q[vertexnum];
	printf("%d ",mGraph.vertex[v]);
	visited[v]=1;
	Q[++rear]=v;
	while(front!=rear)
	{
		v=Q[++front];
		for(i=0;i<vertexnum;i++)
		{
			if(mGraph.arc[v][i]==1&&visited[i]==0)
			{
				printf("%d ",mGraph.vertex[i]);
				visited[i]=1;
				Q[++rear]=i;
			}
		}
	} 
} 
int DFSTraverse2(int v)//基于邻接表的广度优先输出
{
	Arcnode *p;
	int rear=-1,front=-1;
	int Q[vertexnum];
	printf("%d ",adjlist[v].vertex);
	visited[v]=1;
	Q[++rear]=v;
	while(rear!=front)
	{
		v=Q[++front];
		p=adjlist[v].firstedge;
		while(p)
		{
			j=p->adjvex;
			if(visited[j]==0)
			{
				printf("%d ",adjlist[j].vertex);
				visited[j]=1;
				Q[++rear]=j;
			}
			p=p->next;
		} 
	}
} 

void menu()
{
	puts("\t\t0:退出程序");
	puts("\t\t1:创建图的邻接矩阵存储结构");
	puts("\t\t2:创建图的领接表存储方式");
	puts("\t\t3:输出图的邻接矩阵");
	puts("\t\t4:输出图的邻接表");
	puts("\t\t5:在邻接矩阵上深度优先遍历图结构");
	puts("\t\t6:在邻接矩阵上广度优先遍历图结构");
	puts("\t\t7:在邻接表上深度优先遍历图结构");
	puts("\t\t8:在邻接表上广度优先遍历图结构");
	puts("\t\t9:计算并输出图邻接矩阵中结点的度");
	puts("\t\t10:计算并输出图邻接表中结点的度"); 
}
main()
{
/*	int n,e;
	printf("请输入顶点和边数个数\n");
	scanf("%d%d",&n,&e);
	int a[n]="0";
	printf("请输入顶点的元素");
	for(int i=0;i<n;i++)
	scanf("%d",&a[i]);
*/
//	int vertexnum,vertexarc;
//	printf("请输入你的顶点数和边数\n");
//	scanf("%d%d",&vertexnum,&vertexarc);
	menu();
	printf("请输入你要选择的功能: ");
	int n;
	scanf("%d",&n);
	while(n)
	{
	switch(n)
	{ 
	case 0:break;
	case 1:creatGraph(vertexnum,vertexarc); //创建图的邻接矩阵结构
		break;
	case 2:ALgraph(vertexnum,vertexarc);// 创建图的邻接表结构 
		break;
	case 3:display(vertexnum,vertexarc);//输出图的邻接矩阵 
		break;
	case 4:displayAlgraph(vertexnum);//输出图的邻接表
		break;
 	case 5:puts("深度优先遍历输出");
	 		DFSTraversel(0);//基于邻接矩阵的深度优先遍历 
			putchar('\n');//换行 
			initVisited(vertexnum);//初始化visited数组
		break;
	case 6:	DFSTraversel2(0);//基于邻接矩阵的广度优先遍历
 			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	case 7:
			puts("深度优先遍历输出");	
			DFSTraverse(0);//基于邻接表的深度优先遍历
			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	case 8:puts("广度优先遍历输出"); 
			DFSTraverse2(0);//基于邻接表的广度优先遍历
 			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	case 9:tongji(vertexnum);//统计邻接矩阵的出度和入度
		break;
	case 10:tongji2(vertexnum);//统计邻接表的出度和入度
		break;
	}
	menu();
	scanf("%d",&n);
	}
}
  • 因图中可能存在回路,某些顶点可能会被重复访问,那么如何避免遍历不会因回路而陷入死循环。

解决方案:附设访问标志数组visited[n] 。

代码如下:

void initVisited(int vertexNum)
{
	int i;
	for (i=0; i<vertexNum; i++) 
		visited[i] = 0;
} 

邻接矩阵实现图的存储结构

  • 是否可以采用顺序存储结构存储图?

图的特点:顶点之间的关系是m:n,即任何两个顶点之间都可能存在关系(边),无法通过存 储位置表示这种任意的逻辑关系,所以,图无法采用顺序存储结构。

  • 如何存储图?

考虑图的定义,图是由顶点和边组成的,分别考虑如何存储顶点、如何存储边。

  • 邻接矩阵数组表示

基本思想:用一个一维数组存储图中顶点的信息,用一个二维数组(称为邻接矩阵)存储图中各顶点之间的邻接关系。

假设图G=(V,E)有n个顶点,则邻接矩阵是一个n×n的方阵,定义为:

arc[i][j]=        1        若(vi, vj)∈E(或<vi, vj>∈E)
                        0        其它

全局定义结构体

struct node
{ 
	datatype vertex[N];
	int arc[N][N];
	int degree_out[N];  //顶点的出度 
	int degree_in[N];   //顶点的入度  
}mGraph;

邻接矩阵创建图

1.确定图的顶点个数和边的个数;
2. 输入顶点信息;
3. 初始化邻接矩阵;
4. 依次输入每条边存储在邻接矩阵arc中;
     4.1 输入边依附的两个顶点的序号i, j;
     4.2 将邻接矩阵的第i行第j列的元素值置为1;
     4.3 将邻接矩阵的第j行第i列的元素值置为1;

代码如下:

int creatGraph(int vertexnum,int vertexarc)//创建邻接矩阵
{
	int l,k,j,i;
	printf("请输入四个顶点的值\n");
	for(int i=0;i<vertexnum;i++)
	scanf("%d",&mGraph.vertex[i]);
	for(int i=0;i<vertexnum;i++)
	for(int j=0;j<vertexnum;j++)
	mGraph.arc[i][j]=0;
	printf("请输入每条边的两个顶点\n");
	for(i=0;i<vertexarc;i++)
	{
		scanf("%d%d",&l,&k);
		l--;k--;
		mGraph.arc[l][k]=1;	
	}	 
} 

邻接矩阵输出图

代码如下:

void display(int vertexnum,int vertexarc)//输出图的邻接矩阵 
{
	int i,j;
	puts("输出顶点的值");
	for(i=0;i<vertexnum;i++)
	printf("%d  ",mGraph.vertex[i]);
	putchar('\n');
	puts("输出邻接矩阵"); 
	for(i=0;i<vertexnum;i++)
	{for(j=0;j<vertexnum;j++)
		printf("%d  ",mGraph.arc[i][j]);
		putchar('\n');
	}
}

基于邻接矩阵深度优先遍历

⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

代码如下:

int  DFSTraversel(int v)//基于邻接矩阵的深度优先遍历  
{
     printf("%d ",mGraph.vertex[v]); 
     visited [v]=1;
     for (int j=0; j<vertexnum; j++)
         if (mGraph.arc[v][j]==1 && visited[j]==0)
            DFSTraversel( j );
}

基于邻接矩阵的广度优先遍历

⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

代码如下:

int DFSTraversel2(int v)//基于邻接矩阵的广度优先遍历
{
	printf("基于邻接矩阵的广度优先遍历"); 
	int rear=-1,front=-1;
	int Q[vertexnum];
	printf("%d ",mGraph.vertex[v]);
	visited[v]=1;
	Q[++rear]=v;
	while(front!=rear)
	{
		v=Q[++front];
		for(i=0;i<vertexnum;i++)
		{
			if(mGraph.arc[v][i]==1&&visited[i]==0)
			{
				printf("%d ",mGraph.vertex[i]);
				visited[i]=1;
				Q[++rear]=i;
			}
		}
	} 
} 
  • 如何求顶点 i 的所有邻接点?

将数组中第 i 行元素扫描一遍,若arc[i][j]为1,则顶点 j 为顶点 i 的邻接点。

  • 有向图的邻接矩阵一定不对称吗?

不一定,例如有向完全图。

  • 如何判断从顶点 i 到顶点 j 是否存在边?

测试邻接矩阵中相应位置的元素arc[i][j]是否为1。

邻接表实现图的存储结构

  • 图的邻接矩阵存储结构的空间复杂度?

 假设图G有n个顶点e条边,则存储该图需要O(n2) 。

  • 如果为稀疏图则会出现什么现象?

邻接表存储的基本思想:对于图的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,称为顶点vi的边表(对于有向图则称为出边表),所有边表的头指针和存储顶点信息的一维数组构成了顶点表。

  • 边表中的结点表示什么?

每个结点对应图中的一条边,邻接表的空间复杂度为O(n+2e)。

  • 如何求顶点 i 的度?

顶点i的边表中结点的个数。

  • 如何判断顶点 i 和顶点 j之间是否存在边?

测试顶点 i 的边表中是否存在终点为 j 的结点。

  • 如何求顶点 i 的出度?

顶点 i 的出边表中结点的个数。

  • 如何求顶点 i 的入度?

各顶点的出边表中以顶点 i 为终点的结点个数。

  • 如何求顶点 i 的所有邻接点?

遍历顶点 i 的边表,该边表中的所有终点都是顶点 i 的邻接点。

定义邻接表结点

typedef struct ArcNode
{    int adjvex;  
     struct ArcNode *next;
}Arcnode;
struct VertexNode 
{
      datatype  vertex;
      Arcnode *firstedge;
      int degree_out;   //顶点的出度 
      int degree_in;    //顶点的入度 
} adjlist[N];

创建邻接表

1. 确定图的顶点个数和边的个数;
2. 输入顶点信息,初始化该顶点的边表;
3. 依次输入边的信息并存储在边表中;
     3.1  输入边所依附的两个顶点的序号i和j;
     3.2  生成邻接点序号为j的边表结点s;
     3.3 将结点s插入到第i个边表的头部;

代码如下:

int ALgraph(int vertexnum,int vertexarc)//邻接表的创建
 {
 	Arcnode *p;
 	puts("请输入四个顶点的值");
	for(i=0;i<vertexnum;i++)
	{
		scanf("%d",&adjlist[i].vertex);
		adjlist[i].firstedge=NULL;
	}	
	puts("输入每条边的两个顶点");
	int k,l;//用于接收边的两个顶点 
	for(int j=0;j<vertexarc;j++)
	{	
		scanf("%d%d",&k,&l);
		k--;l--;
		adjlist[k].degree_out++;
		adjlist[l].degree_in++;
		p=(Arcnode *)malloc(sizeof(Arcnode));
		p->adjvex=l;
		p->next=adjlist[k].firstedge;
		adjlist[k].firstedge=p;
	}
 } 

输出邻接表

代码如下:

void displayAlgraph(int vertexnum)//邻接表的输出 
{	
	ArcNode *p;
	for(i=0;i<vertexnum;i++)
	{
		p=adjlist[i].firstedge;
		printf("%d :",i+1);
		while(p!=NULL)
		{
			//printf("%d ",p->adjvex);
			printf("(%d,%d)  ",adjlist[i].vertex,adjlist[p->adjvex].vertex);
			p=p->next;
		}
		printf("\n");
	}
} 

基于邻接表的深度优先遍历

⑴ 访问顶点v;
⑵ 从v的未被访问的邻接点中选取一个顶点w,从w出发进行深度优先遍历;
⑶ 重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。

代码如下:

int DFSTraverse(int v)//基于邻接表的深度优先遍历
{
	Arcnode *p;
	p=adjlist[v].firstedge;
	printf("%d ",adjlist[v].vertex);
	visited[v]=1;
	while(p)
	{
		j=p->adjvex;
		if(visited[j]==0)
		DFSTraverse(j);
		p=p->next;
	}
	
} 

基于邻接表的广度优先遍历

⑴ 访问顶点v;
⑵ 依次访问v的各个未被访问的邻接点v1, v2, …, vk;
⑶ 分别从v1,v2,…,vk出发依次访问它们未被访问的邻接点,并使“先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问。直至图中所有与顶点v有路径相通的顶点都被访问到。

代码如下:

int DFSTraverse2(int v)//基于邻接表的广度优先输出
{
	Arcnode *p;
	int rear=-1,front=-1;
	int Q[vertexnum];
	printf("%d ",adjlist[v].vertex);
	visited[v]=1;
	Q[++rear]=v;
	while(rear!=front)
	{
		v=Q[++front];
		p=adjlist[v].firstedge;
		while(p)
		{
			j=p->adjvex;
			if(visited[j]==0)
			{
				printf("%d ",adjlist[j].vertex);
				visited[j]=1;
				Q[++rear]=j;
			}
			p=p->next;
		} 
	}
} 

主函数

main()
{menu();
	printf("请输入你要选择的功能: ");
	int n;
	scanf("%d",&n);
	while(n)
	{
	switch(n)
	{ 
	case 0:break;
	case 1:creatGraph(vertexnum,vertexarc); //创建图的邻接矩阵结构
		break;
	case 2:ALgraph(vertexnum,vertexarc);// 创建图的邻接表结构 
		break;
	case 3:display(vertexnum,vertexarc);//输出图的邻接矩阵 
		break;
	case 4:displayAlgraph(vertexnum);//输出图的邻接表
		break;
 	case 5:puts("深度优先遍历输出");
	 		DFSTraversel(0);//基于邻接矩阵的深度优先遍历 
			putchar('\n');//换行 
			initVisited(vertexnum);//初始化visited数组
		break;
	case 6:	DFSTraversel2(0);//基于邻接矩阵的广度优先遍历
 			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	case 7:
			puts("深度优先遍历输出");	
			DFSTraverse(0);//基于邻接表的深度优先遍历
			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	case 8:puts("广度优先遍历输出"); 
			DFSTraverse2(0);//基于邻接表的广度优先遍历
 			putchar('\n');//换行
			initVisited(vertexnum);//初始化visited数组
		break;
	}
	menu();
	scanf("%d",&n);
	}
}
 

  • 28
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值