一、图的存储结构
我们知道,数据之间的关系可分为3种,分别是“一对一”、“一对多”、“多对多”,前两种关系可分别用数组和树存储,而多对多的数据结构则需要用图来存储。
图是用(V,E)来表示的,其中V是顶点的集合,E是边的集合。对于图的存储,主要有邻接矩阵和邻接表两种结构。
二、邻接矩阵
**邻接矩阵是一个存储着边信息的矩阵,顶点用矩阵下标表示。**对于矩阵M,如果M(i,j)=1,则顶点i和顶点j之间存在一条边;若M(i,j)=0,则顶点i与顶点j之间没有边;
对于无向图来说,若M(i,j)=1,则M(j,i)=1.也就是邻接矩阵是一个对称矩阵。而对于有向图来说,则不一定是一个对称矩阵。
**此结构适用于稠密图。**因为对于顶点数很多边很少的稀疏图,用这种结构会浪费空间,即矩阵中为1的元素很少,却占用了很大的空间。下面用代码一步步实现图的邻接矩阵存储。
**邻接矩阵由顶点数、边数、定点集合、边集合组成。**定点集合为一维数组,边集合为二维数组。为节省空间,我们根据顶点的数量动态分配数组的大小。
typedef struct juzhen
{
int num1;//顶点数
int num2;//边数
dingdianleixing *a;//顶点集合 bianleixing **b;//边集合
}juzhen;
下面是矩阵的具体构造
void creatjuzhen(juzhen *g)
{
int i,j,k;
printf("请输入顶点数和边数:");
scanf("%d %d",&g->num1,&g->num2);
//动态生成顶点数组的大小
g->a=(dingdianleixing *)malloc(g->num1*sizeof(dingdianleixing));
//动态生成边二维数组的大小
g->b=(bianleixing**)malloc(g->num1*sizeof(bianleixing)); for(i=0;i<g->num1;i++)
{
g->b[i]=(bianleixing*)malloc(g->num1*sizeof(bianleixing));
}
//初始化矩阵所有元素为0
for(i=0;i<g->num1;i++)
for(j=0;j<g->num1;j++)
{
g->b[i][j]=0;
}
//输入边的信息
for(k=0;k<g->num2;k++)
{
scanf("%d %d",&i,&j);
g->b[i][j]=1;
g->b[j][i]=1;
}
//输出矩阵
for(i=0;i<g->num1;i++)
{
for(j=0;j<g->num1;j++)
{
printf("%d\t",g->b[i][j]);
}
printf("\n");
}
}
邻接矩阵结构的完整代码
#include
#include
typedef char dingdianleixing;
typedef int bianleixing;
typedef struct juzhen
{
int num1;//顶点数
int num2;//边数
dingdianleixing *a;//顶点集合
bianleixing **b;//边集合
}juzhen;
void creatjuzhen(juzhen *g)
{
int i,j,k;
printf("请输入顶点数和边数:");
scanf("%d %d",&g->num1,&g->num2);
//动态生成顶点数组的大小
g->a=(dingdianleixing *)malloc(g->num1*sizeof(dingdianleixing)); //动态生成边二维数组的大小
g->b=(bianleixing**)malloc(g->num1*sizeof(bianleixing)); for(i=0;i<g->num1;i++)
{
g->b[i]=(bianleixing*)malloc(g->num1*sizeof(bianleixing));
}
//初始化矩阵所有元素为0
for(i=0;i<g->num1;i++)
for(j=0;j<g->num1;j++)
{
g->b[i][j]=0;
}
//输入边的信息
for(k=0;k<g->num2;k++)
{
scanf("%d %d",&i,&j);
g->b[i][j]=1;
g->b[j][i]=1;
}
//输出矩阵
for(i=0;i<g->num1;i++)
{
for(j=0;j<g->num1;j++)
{
printf("%d\t",g->b[i][j]);
}
printf("\n");
}
}
int main()
{
juzhen *g;
g=(juzhen*)malloc(sizeof(juzhen));
creatjuzhen(g);
}
三、图的邻接表存储
对于边数相对于顶点较少的图,邻接矩阵会浪费空间,为此,我们可以考虑用链表来动态分配空间。
对于来说邻接表,顶点用一维数组来存储,称为顶点节点。每个数组元素除了存储顶点数据以外,还会存储指向第一个邻接点的指针f。与每个顶点节点相连接的顶点称为边节点,边节点的结构体包括顶点对应的下标和指向下一个边节点的指针。
//边节点的结构体
typedef struct s1
{
int p1;
struct s1*next;
}s1;
//顶点节点
typedef struct s2{
dingdianleixing v;
s1*f;//指向第一条边
}s2;
对于整体的邻接表,只要包括顶点数、边数以及顶点集合。
typedef struct linjiebiao
{ int num1;//顶点数量
int num2;//边数量
s2*v;//顶点集合
}linjiebiao;
下面是邻接表的具体创建
void linjiebiao_creat(linjiebiao *g)
{ int i,j,k;
scanf("%d %d",&g->num1,&g->num2);
g->v=(s2*)malloc(g->num1*sizeof(s2)); //初始化顶点所指的第一条边 for(i=0;inum1;i++)
{
g->v[i].f=NULL;
}
//输入图的信息
s1* s8;
for(k=0;k<g->num2;k++)
{
scanf("%d %d",&i,&j); //始终将插入的节点放在顶点所指的第一条边
//将顶点j连在顶点i之后
s8=(s1*)malloc(sizeof(s1));
s8->p1=j;
s8->next=g->v[i].f;
g->v[i].f=s8; //将顶点i连在顶点j之后 s8=(s1*)malloc(sizeof(s1));
s8->p1=i;
s8->next=g->v[j].f;
g->v[j].f=s8;
}
//输出邻接表
s1 *len=NULL;
for(i=0;i<g->num1;i++)
{
if(g->v[i].f!=NULL)
{
len=g->v[i].f;
}
while(len!=NULL)
{
printf("%d--%d\t",i,len->p1);
len=len->next;
}
printf("\n");
}
}
完整代码
#include
#include
typedef char dingdianleixing;
typedef int bianleixing;
//边节点的结构体
typedef struct s1{
int p1;
struct s1*next;}s1;
//顶点节点
typedef struct s2{
dingdianleixing v;
s1*f;//指向第一条边
}s2;
typedef struct linjiebiao{
int num1;//顶点数量
int num2;//边数量
s2*v;//顶点集合
}linjiebiao;
void linjiebiao_creat(linjiebiao *g)
{
int i,j,k;
scanf("%d %d",&g->num1,&g->num2);
g->v=(s2*)malloc(g->num1*sizeof(s2)); //初始化顶点所指的第一条边 for(i=0;inum1;i++)
{
g->v[i].f=NULL;
}
//输入图的信息
s1* s8;
for(k=0;k<g->num2;k++)
{
scanf("%d %d",&i,&j); //始终将插入的节点放在顶点所指的第一条边
//将顶点j连在顶点i之后
s8=(s1*)malloc(sizeof(s1));
s8->p1=j;
s8->next=g->v[i].f;
g->v[i].f=s8; //将顶点i连在顶点j之后 s8=(s1*)malloc(sizeof(s1));
s8->p1=i;
s8->next=g->v[j].f;
g->v[j].f=s8;
}
//输出邻接表
s1 *len=NULL;
for(i=0;i<g->num1;i++)
{
if(g->v[i].f!=NULL)
{
len=g->v[i].f;
}
while(len!=NULL)
{
printf("%d--%d\t",i,len->p1);
len=len->next;
}
printf("\n");
}
}
int main()
{
linjiebiao *g;
g=(linjiebiao *)malloc((sizeof(linjiebiao))); linjiebiao_creat(g);
}