实现基于邻接矩阵实现的图的创建和基本操作,其中本代码创建的为带权无向图
图的定义:
图是一种非线性数据结构,其数据元素之间的关系没有限制,任意两个元素之间都有可能有某种关系。数据元素用结点(node)表示,如果相关,就用一条边(edge)将相应的结点连接起来,这两个结点称为相邻节点。
这样图就可以定义为由结点集合及结点间的关系集合组成的一种数据结构。下图展示了几个图的示例,结点又称为顶点(vertex),结点之间的关系称为边,一个图G记为G=(V,E),其中V是结点的有限集合,E是边的有限集合
代码实现,定义的结构体为
typedef struct AMGraph{
VerTexType vexs[MVNum];//顶点用数组存储
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum;//点个数
int arcsnum;//边个数
}AMGraph;
在代码调用的函数为
int locateVex(AMGraph G, VerTexType u);//返回顶点位置
int creataUDN(AMGraph& G);//创建图
void DFS(AMGraph G, int v);//深度优先遍历算法
void DFSTraverse(AMGraph G);//深度优先遍历初始化并调用DFS
int OperateMenu();//打印用户界面并返回选择
void printMatrix(AMGraph G);//打印邻接矩阵
void BFSTraverse(AMGraph G);//广度优先遍历初始化并调用BFS
void BFS(AMGraph G, int v);//广度优先遍历算法
void MiniSpanTree_Prim(AMGraph G);//普利姆算法最小生成树
完整代码为:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define MVNum 100//定义最大定点数为100
#define MaxInt 32767//定义极大值为无穷
#define OK 1
#define ERROR -1
typedef char VerTexType;//定义存储的数据类型为字符型
typedef int ArcType;//边权值为整型
typedef struct AMGraph{
VerTexType vexs[MVNum];//顶点用数组存储
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum;//点个数
int arcsnum;//边个数
}AMGraph;
int locateVex(AMGraph G, VerTexType u);//返回顶点位置
int creataUDN(AMGraph& G);//创建图
void DFS(AMGraph G, int v);//深度优先遍历算法
void DFSTraverse(AMGraph G);//深度优先遍历初始化并调用DFS
int OperateMenu();//打印用户界面并返回选择
void printMatrix(AMGraph G);//打印邻接矩阵
void BFSTraverse(AMGraph G);//广度优先遍历初始化并调用BFS
void BFS(AMGraph G, int v);//广度优先遍历算法
void MiniSpanTree_Prim(AMGraph G);//普利姆算法最小生成树
int main()
{
AMGraph G;
int code;
do {
code = OperateMenu();
switch (code)
{
case 0:
printf("程序已退出。\n");
exit(0);
case 1:
if (creataUDN(G))
printf("初始化成功\n");
break;
case 2:
DFSTraverse(G);
break;
case 3:
BFSTraverse(G);
break;
case 4:
printMatrix(G);
break;
case 5:
MiniSpanTree_Prim(G);
break;
}
} while(code!=0);
return 0;
}
//邻接矩阵创建无向图
int creataUDN(AMGraph& G)
{
printf("请输入总顶点数和总边数\n");
rewind(stdin);
scanf("%d %d",&G.vexnum,&G.arcsnum);
printf("请依次输入顶点的信息\n");
//依次输入信息到顶点数组
for (int i = 0; i < G.vexnum; i++)
{
printf("第%d个顶点为\n", i + 1);
rewind(stdin);
scanf("%c", &G.vexs[i]);
}
//初始化矩阵,同意将权值设置为最大值
for (int i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
{
G.arcs[i][j] = MaxInt;
}
}
//依次为矩阵赋值
for (int i = 0; i < G.arcsnum; i++)
{
printf("请输入一条边所依附的两个顶点及其权值\n");
char v1, v2;
int w;//点1,点2,权值
rewind(stdin);
scanf("%c %c %d", &v1, &v2, &w);
int temp1 = locateVex(G, v1);//v1在顶点数组中的位置
int temp2 = locateVex(G, v2);//v2在顶点数组中的位置
if (temp1 == -1 || temp2 == -1)
printf("未在图中找到该顶点,请重新输入\n");
else
{
G.arcs[temp1][temp2] = w;
G.arcs[temp2][temp1] = G.arcs[temp1][temp2];
}
}
return OK;
}
//返回顶点在顶点数组的位置,顶点不存在返回Eorro
int locateVex(AMGraph G, VerTexType u)
{
for (int i = 0; i < G.vexnum; i++) {
if (u == G.vexs[i])
return i;//查找到返回位置
}
return ERROR;//没查找到返回0
}
#define MAXV 100
typedef int Bool;
#define TRUE 1
#define FALSE 0
Bool visited[MAXV]; // 用来标记顶点是否被访问过
// 深度优先遍历函数
void DFS(AMGraph G, int v) {
visited[v] = TRUE; // 标记顶点v为已访问
//printf("%s ", G.vexs[v]); // 访问顶点v
for (int w = 0; w < G.vexnum; w++) {
if (G.arcs[v][w] != MaxInt && !visited[w]) {
DFS(G, w); // 递归访问顶点w的邻接顶点
}
}
}
void DFSTraverse(AMGraph G) {
for (int v = 0; v < G.vexnum; v++) {
visited[v] = FALSE; // 初始化visited数组
}
for (int v = 0; v < G.vexnum; v++) {
if (!visited[v]) {
DFS(G, v); // 对未访问的顶点进行深度优先遍历
}
}
printMatrix(G);
}
/**/
void printMatrix(AMGraph G)
{
// 打印邻接矩阵
printf("邻接矩阵:\n");
printf(" ");
for (int i = 0; i < G.vexnum; i++)
printf(" %c", G.vexs[i]);
printf("\n");
for (int i = 0; i < G.vexnum; i++) {
printf("| ");
for (int j = 0; j < G.vexnum; j++) {
if (G.arcs[i][j] == MaxInt) {
printf("0 ");
}
else {
printf("%d ", G.arcs[i][j]);
}
}
printf("|\n");
}
}
void BFSTraverse(AMGraph G)
{
for (int v = 0; v < G.vexnum; v++)
{
visited[v] = FALSE;
}
for (int v = 0; v < G.vexnum; v++)
{
if (!visited)//节点未被遍历,调用广度优先遍历
BFS(G, v);
}
printMatrix(G);
}
//广度优先遍历
void BFS(AMGraph G, int v)
{
int queue[MVNum];//定义一个队列用与存放顶点的位置
int front = 0;//队首初始化
int rear = 0;//队尾初始化;
int flag;//接住队首顶点坐标的临时变量
visited[v] = TRUE;//将顶点v的状态重置为true
rear = (rear + 1) % MVNum;//队尾加1,%mvnum防止队列溢出
queue[rear] = v;//将v入队
while (front != rear)
{
front = (front + 1) % MVNum;//出队,队首后移
flag = queue[front];//接住队首顶点位置
for (int i = 0; i < G.vexnum; i++) //遍历与v相关的每个顶点
{
if (G.arcs[flag][i] != 0 && visited[flag] != TRUE)//边存在且顶点未被访问
{
visited[flag] = 1;//更新状态
rear = (rear + 1) % MVNum;//队尾加1
queue[rear] = i;//将其加入队列
}
}
}
}
//最小生成树 普利姆算法
void MiniSpanTree_Prim(AMGraph G) {
int lowcost[MVNum]; // 存储顶点到集合U的最小权值
int adjvex[MVNum]; // 存储相关顶点下标
// 初始化lowcost和adjvex数组
for (int i = 0; i < G.vexnum; i++) {
lowcost[i] = G.arcs[0][i]; // 将v0顶点的权值存入数组
adjvex[i] = 0; // 初始化为v0的下标
}
for (int i = 1; i < G.vexnum; i++) {
int min = MaxInt; // 初始化最小权值为无穷
int j = 0, k = 0;
for (int m = 1; m < G.vexnum; m++) {
if (lowcost[m] != 0 && lowcost[m] < min) {
min = lowcost[m];
j = m;
}
}
printf("生成树的一条边(顶点,顶点,边的权值)");
printf("(%c,%c,%d)\n", G.vexs[adjvex[j]], G.vexs[j], lowcost[j]); // 输出生成树的边
lowcost[j] = 0; // 将选定的顶点的权值设为0,表示此顶点已经完成任务
for (int m = 1; m < G.vexnum; m++) {
if (G.arcs[j][m] < lowcost[m]) { // 若从j到m的权值小于此前这个顶点到m的权值
lowcost[m] = G.arcs[j][m]; // 更新权值
adjvex[m] = j; // 更新下标
}
}
}
}
int OperateMenu(){
int code;
printf("=========================================================\n");
printf("请选择操作:\n");
printf("0. 退出\n");
printf("1. 链接矩阵创建图\n");
printf("2. 深度优先遍历\n");
printf("3. 广度优先遍历\n");
printf("4. 打印链接矩阵\n");
printf("5. 普利姆算法最小生成树\n");
printf("6. \n");
printf("7. \n");
printf("8. \n");
printf("=========================================================\n");
printf("请输入操作代码:");
scanf("%d", &code);
return code;
}