要谈邻接表,那我们先谈谈邻接矩阵,因为邻接表就是因为邻接矩阵对于稀疏图造成内存的很大浪费。那么它是如何浪费的哪? 别急慢慢来!
- 什么叫做邻接矩阵?
它就是存储顶点是否存在连接关系的二维数组如下(右),关系图(左)
解释图:如果a1->a2有线连接的话,用1表示反之用0表示。
代码如下:
#define MAX_VERTEX_NUM 20 /*最多顶点数目*/
#define INFINITY 32768 /*表示最大值*/
#include <stdio.h>
#include <stdlib.h>
/*图的种类: DG表示有向图 DN表示有向网 UDG表示无向图 UDN表示无向网*/
typedef enum {DG,DN,UDG,UDN} GraphKind;
typedef int VertexData;//假设顶点数据为字符型
typedef struct ArcNode//存储邻接结点的信息
{
int adj; //对于无权图,用1或0表示是否相邻;对带权图,则为表示权值类型
int info; //暂时不知道存储什么。
}ArcNode;
typedef struct
{
VertexData vertex[MAX_VERTEX_NUM];
ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵
int vexnum, arcnum;//图放入顶点数和弧数
GraphKind kind;//图的种类标志
}AdjMatrix;//邻接矩阵信息
int locateVertex(AdjMatrix *G, VertexData v)//找顶点函数
{
int j = 0, k;
for (k = 0; k < G->vexnum; k++)
{
if (G->vertex[k] == v)
{
j = k; break;
}
}
return (j);
}
int main()
{
AdjMatrix *G;
G = (AdjMatrix *)malloc(sizeof(AdjMatrix));//定义对象
int i, j, k, weight;
VertexData v1=0, v2=0;//两顶点。
printf("请输入图的顶点数和弧数\n");
scanf_s("%d,%d", &G->vexnum, &G->arcnum);
getchar();
for (i = 0; i < G->vexnum; i++)//初始化邻接矩阵
{
for (j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = 0;//权值最大
}
}
printf("请输入顶点的数据\n");
for (i = 0; i < G->vexnum; i++)
{
scanf_s("%d", &G->vertex[i]);
}
printf("请输入两个顶点和权值\n");
for (k = 0; k < G->arcnum; k++)//两个顶点建立联系
{
scanf_s("%d,%d,%d", &v1, &v2, &weight);//读入数据
i = locateVertex(G, v1);
j = locateVertex(G, v2);
G->arcs[i][j].adj = weight;//赋权值
}//建弧完成
printf("打印出带权图结构如下\n");
for (i = 0; i < G->vexnum; i++)
{
for (j = 0; j < G->vexnum; j++)
{
printf("%d ", G->arcs[i][j].adj);
}
printf("\n");
}
system("pause");
return 0;
}
运行结果如下:
从图中我们发现存了不少0,表示它们不是邻接关系,但是我们并不需要记住它们不相邻,所以这就大大浪费了内存。所以就有了邻接表法存储。
- 邻接法
优点:只存储关联的信息,对于图中存在的边信息,则存储,而不相邻的顶点则不保留信息。
图奉上如下:
后面的结点我只分了两份,实际还要给权值给开个空间存储信息。(我给省略了)。
代码如下:
//邻接表存储结构的形式
#include <stdio.h>
#include<stdlib.h>
#define MAX_VERTEX_NUM 20
#define VertexData int
#define max 100
#define size 4//存储数字的大小
#define bian 4//存储边的大小
int edge[1000][2] = { 0 };
typedef enum{DG,DN,UDG,UDN} GraphKind;//图的种类
typedef struct ArcNode
{
int data;
struct ArcNode *nextarc; //指向下一条弧的指针
//int weight;//权值 先不考虑
}ArcNode;
typedef struct VertexNode
{
VertexData data;//顶点数据
ArcNode *firstarc;//指向该顶点第一条弧的指针
}VertexNode;
typedef struct
{
VertexNode vertex[MAX_VERTEX_NUM];//一维顶点数组
int vexnum, arcnum;//顶点个数和弧数
}AdjList;//邻接目录
int find_insert(ArcNode *edge, ArcNode *temp)
{
ArcNode *p;
p = edge;
while (p->nextarc != NULL)//循环找到一维顶点数组中的每一个顶点之后的邻接点
{
p = p->nextarc;
}
p->nextarc = temp;//插入找到的边
return 0;
}
AdjList* CreGraph()//创建图
{
int i,c1,c2;
ArcNode *Arc;
Arc = (ArcNode*)malloc(sizeof(ArcNode));
AdjList *picture;
picture = (AdjList*)malloc(sizeof(AdjList));
picture->vexnum = size;
picture->arcnum = bian;
printf("请输入顶点\n");
for (i = 1; i <= picture->vexnum; i++)
{
scanf_s("%d", &picture->vertex[i].data);
picture->vertex[i].firstarc = NULL;//刚开始把一维数组建立起来
}
//再确定哪两个顶点连接需要存边0代表出度连接1代表入度
for (int i = 1; i <= picture->arcnum; i++)//建立联系
{
c1 = edge[i][0];
c2 = edge[i][1];
Arc = (ArcNode*)malloc(sizeof(ArcNode));//新申请的结点
Arc->data = edge[i][1];//获取顶点数
Arc->nextarc = NULL;
//从输入的弧中将入度相同的结点连接起来
if (picture->vertex[c1].firstarc == NULL)
{
picture->vertex[c1].firstarc = Arc;
}
else
{
find_insert(picture->vertex[c1].firstarc, Arc);
}
}
return picture;
}
int main()
{
int i = 0;
AdjList* picture;
printf("请输入要连接的序号边\n");
for (i = 1; i <= bian ; i++)
{
scanf("%d",&edge[i][0]);//代表号,不是放权值
scanf("%d", &edge[i][1]);
}
picture = CreGraph();
printf("邻接表如下\n");
for (int i = 1; i <= picture->vexnum; i++)
{
ArcNode *p;
p = (ArcNode*)malloc(sizeof(ArcNode));
if (picture->vertex[i].firstarc == NULL)
{
printf("error");
}
else
{
p = picture->vertex[i].firstarc;
printf("%d ", picture->vertex[i].data);
while (p != NULL)
{
printf("%d ", p->data);
p = p->nextarc;
}
}
printf("\n");
}
system("pause");
return 0;
}
代码有点长,但对于IT专业的我们来说,耐的住心慢慢理解,到最终get it,是非常重要的,加油!