图的数组表示法(邻接矩阵)
图的邻接矩阵存储方式:用两个数组来表示图,一个一维数组存储图中顶点的信息,一个二维数组存储图中边的信息。对于网(边上带权的图),二维数组中存储权值,无连接的顶点之间权值为infinity(无穷大)。
代码实现如下:
#include "head.h"
#define INFINITY 65535
#define MAX_VERTEX_NUM 20 //最大顶点个数
typedef enum GraphKind{DG,DN,UDG,UDN}; //有向图,有向网,无向图,无向网
typedef char Elemtype;
typedef struct ArcCell{
int adj; //对图,1/0表示是否邻接;对网,用数字表示权值
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//以结构体为元素的**弧数组**
typedef struct{
Elemtype vexs[MAX_VERTEX_NUM];//**顶点数组**
AdjMatrix arcs; //邻接矩阵(弧数组)
int vexnum,arcnum; //顶点数,弧数
GraphKind kind; //图的类型
}MGraph; //邻接矩阵
void CreateGraph(MGraph *G)
{
int kind;
printf("输入图的类型对应的序号(0.DG/1.DN/2.UDG/3.UDN):");
scanf_s("%d",&kind);
switch (kind)
{
case 0:CreateDG(G); break;
case 1:CreateDN(G); break;
case 2:CreateUDG(G); break;
case 3:CreateUDN(G); break;
default :return ERROR;
}
}
int LocateVex(MGraph *G,Elemtype point)//确定顶点point在图G(顶点数组)中的序号
{
for(int i = 0;i<G->vexnum;i++)
if(G->vexs[i] == point)
return i;
}
void CreateUDN(MGraph *G) //无向网
{
int weight; //边的权值
Elemtype origin,terminus; //边的起点、终点
printf("请输入顶点数与弧数:\n");
scanf_s("%d %d",&(G->vexnum),&(G->arcnum));
for(int i = 0;i<G->vexnum;i++)
{
printf("输入顶点值:\n");
scanf_s("%c",&(G->vexs[i]));
}
for(int i = 0;i<G->vexnum;i++) //邻接矩阵初始化
for(int = 0;j<G->vexnum;j++)
{
G->arcs[i][j].adj = INFINITY;
}
for(int k = 0;k<G->arcnum;k++) //邻接矩阵赋值
{
printf("请输入一条边的两端及权值:\n");
scanf_s("%c %c %d",&origin,&terminus,&weight);
int i = LocateVex(G,origin);
int j = LocateVex(G,terminus);
G->arcs[i][j].adj = weight;
G->arcs[j][i].adj = weight;
}
printf("创建成功\n");
}
void CreateUDG(MGraph *G) //无向图
{
int origin, terminus; //边的起点,终点
printf("输入图的顶点数、弧数:");
scanf_s("%d %d", &(G->vexnum), &(G->arcnum));
for (int i = 0; i < G->vexnum; i++) //将顶点值存储进去
{
printf("输入顶点值:");
scanf_s("%c", &(G->vexs[i]));
}
for (int i = 0; i < G->vexnum; i++) //初始化邻接矩阵
for (int j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = 0;
}
for (int k = 0; k < G->arcnum; k++)
{
printf("输入一条边的两端");
scanf_s("%c %c", &origin, &terminus);
int i = LocateVex(G, origin);
int j = LocateVex(G, terminus);
G->arcs[i][j].adj = 1;
G->arcs[j][i].adj = 1;
}
printf("创建成功\n");
}
void CreateDG(MGraph *G)//有向图
{
int origin, terminus;
printf("输入图的顶点数,弧数:\n");
scanf_s("%d %d",&(G->vexnum),&(G->arcnum));
for (int i = 0; i < G->vexnum; i++) //顶点赋值
{
printf("输入顶点值:");
scanf_s("%c",&(G->vexs[i]));
}
for (int i = 0; i < G->vexnum; i++) //初始化邻接矩阵
{
for (int j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = 0;
}
}
for (int k = 0; k < G->arcnum; k++)
{
printf("输入一条边的起点终点");
scanf_s("%c %c", &origin, &terminus);
int i = LocateVex(G, origin);
int j = LocateVex(G, terminus);
G->arcs[i][j].adj = 1;
}
printf("创建成功\n");
}
void CreateDN(MGraph *G) //有向网
{
int origin, terminus, weight; //边的起点,终点,权值
printf("输入图的顶点数 弧数");
scanf_s("%d %d", &(G->vexnum), &(G->arcnum));
for (int i = 0; i < G->vexnum; i++) //将顶点值存储进去
{
printf("输入顶点值:");
scanf_s("%c", &(G->vexs[i]));
}
for (int i = 0; i < G->vexnum; i++) //初始化邻接矩阵
for (int j = 0; j < G->vexnum; j++)
{
G->arcs[i][j].adj = INFINITY;
}
for (int int k = 0; k < G->arcnum; k++)
{
printf("输入一条边的起点、终点以及权值");
scanf_s("%c %c %d", &origin, &terminus, &weight);
i = LocateVex(G, origin);
j = LocateVex(G, terminus);
G->arcs[i][j].adj = weight;
}
printf("创建成功\n");
}
图的邻接表
邻接表是图的的一种链式存储结构。邻接表由结点表(数组)和边表(链表)组成。有向图及无向图的邻接表示意图如图所示:
有时为了确定顶点的入度/指向顶点的弧,会建立有向图的逆邻接表,如下所示:
代码实现如下:
#include "head.h"
#define MAX_VERTEX_NUM 20
typedef char Elemtype;
typedef enum Graphtype{DG,DN,UDG,UDN};//
typedef struct ArcNode{
int adjvex; //该弧指向的顶点 在结点表中的位置
ArcNode *nextarc; //指向下一条弧的指针
int info; //权值
}ArcNode;//边表结点
typedef struct VerNode{
Elemtype data; //顶点存储的数据
ArcNode *firstarc; //指向第一个边结点的指针
}VerNode,AdjList[MAX_VERTEX_NUM];//结点表
typedef struct{
AdjList vertices; //结点表
int vexnum,arcnum; //顶点个数、弧个数
Graphtype kind; //图的类型
}ALGraph,*Graph;
void CreateALGraph(ALGraph *G)
{
ArcNode *arc;
printf("输入顶点和边数:");
scanf_s("%d %d",&G->vexnum,&G->arcnum);
for(int i = 0;i < G->vexnum; i++) //建立结点表
{
scanf_s("%c",&G->vertices[i].data);
G->vertices[i].firstarc = NULL;
}
for(int j = 0;j<G->arcnum;j++) //建立边表
{
int weight = 0; //弧的权重
int start,end; //弧的起点和终点
arc = (ArcNode*)malloc(sizeof(ArcNode)); //建立边结点
scanf_s("%d %d %d",&start,&end,&weight);
arc->info = weight; //给边赋权值
arc->adjvex = end; //弧指向的顶点(所在结点表位置序号)
arc->nextarc = G->vertices[start].firstarc; //该弧结点指向与结点表相连的弧
G->vertices[start].firstarc = arc; //结点表指针域指向该弧结点
}
}
图的十字链表
十字链表是有向图的另一种链式存储结构,可以看成邻接表和逆邻接表结合起来的一种链表。
存储结构如图所示:
#include "head.h"
#define MAX_VERTEX_NUM 20
typedef char Elemtype;
typedef struct ArcNode{
int tailvex,headvex; //弧的头尾结点在顶点表中的序号
ArcNode *hlink,*tlink;//弧的头/尾相同的下一条弧
int info; //弧的权值
}ArcNode;
typedef struct VexNode{
Elemtype data; //顶点的数据域
ArcNode *firstin,*firstout;//顶点的链域
}VexNode;
typedef struct{
VexNode Vexlist[MAX_VERTEX_NUM];//顶点结点表
int vexnum,arcnum; //顶点、弧的个数
}OLGraph;
int LocateVex(OLGraph *G,Elemtype data)//定位某元素在顶点表中的序号
{
for(int i = 0; i < G->vexnum; i++)
if(G->Vexlist[i] == data)
return i;
}
void CreateOLGraph(OLGraph *G)
{
printf("请输入顶点和弧的数量:\n");
scanf_s("%d %d",&G->vexnum,&G->arcnum);
for(int i=0;i<G->vexnum;i++)
{
printf("请依次输入顶点数据:");
scanf_s("%c",&G->Vexlist[i].data); //输入数据
G->Vexlist[i].firstin = NULL; //初始化指针域
G->Vexlist[i].firstout = NULL;
}
ArcNode *p;
Elemtype vstart,vend,weight;
for(int k = 0;k < G->arcnum;k++)
{
printf("请依次输入弧的起点和终点对应的数据以及权值:");
scanf_s("%c %c %d",&vstart,&vend,&weight);
int i = LocateVex(G,vstart);
int j = LocateVex(G,vend);
p = (ArcNode*)malloc(sizeof(ArcNode));//构造弧结点
p->info = weight;
p->headvex = i;
p->tailvex = j;
p->hlink = G->Vexlist[i].firstin; //让此弧结点指向此时与顶点相连的弧
p->tlink = G->Vexlist[j].firstout;
G->Vexlist[i].firstin = p; //使顶点链域改为指向该弧结点
G->Vexlist[j].firstout = p;
}
}
邻接多重表
邻接多重表是无向图的一种链式存储结构,它与十字链表有些类似,弧结点多了一个标志域,用来记录该边是否被搜索过,每个顶点结点则只有一个指向弧的指针域(因为是无向图,不需要考虑出度和入度)。