图的存储结构与操作--C语言实现
图(graph)是一种比树结构还要复杂的数据结构,它的术语,存储方式,遍历方式,用途都比较广,所以如果想要一次性完成所有的代码,那代码会非常长。所以,我将分两次来完成图的代码。这一次,我会完成图的五种存储结构的创建(邻接矩阵存储,邻接表存储,十字链表存储,邻接多重表存储,边集数组存储),两种遍历方式(深度优先遍历,广度优先遍历)。与树结构一样,图结构的遍历也需要借助队列来协助实现。
#include<stdio.h>
#include<malloc.h>
typedef char VertexType; //顶点类型
typedef int EdgeType; //权值类型
#define MAXVEX 100 //最大顶点数
#define INFINITY 65535 //无限大
#define TURE 1
#define FALSE 0
typedef int Boolean;
Boolean visited[MAXVEX]; //记录访问过的结点数组 #define MAXSIZE 100 //队列最大空间
typedef int QElemType; //队列中元素类型
typedef int Status; //返回值类型
#define OK 1 //操作成功
#define ERROR 0 //操作失败 typedef struct //队列结点结构
{
QElemType date[MAXSIZE]; //结点元素
int front; //队头
int rear; //队尾
}SqQueue; /*队列的初始化*/
Status InitQue(SqQueue *Q)
{
Q->front = ; //队头指向0
Q->rear = ; //队尾指向0
return OK;
} /*队列的入队操作*/
Status EnQueue(SqQueue *Q, QElemType e)
{
if((Q->rear + ) % MAXSIZE == Q->front) //判断队列是否已满
return ERROR;
Q->date[Q->rear] = e; //队尾赋值为e
Q->rear = (Q->rear + ) % MAXSIZE; //队尾后移
return OK;
} /*队列的出队操作*/
Status DeQueue(SqQueue *Q, QElemType *e)
{
if(Q->front == Q->rear) //判断队列是否为空
return ERROR;
*e = Q->date[Q->front]; //将队头元素取出
Q->front = (Q->front + ) % MAXSIZE; //队头后移
return OK;
} /*获取队列的长度*/
int LengthQue(SqQueue Q)
{
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
} //邻接矩阵结构体
typedef struct
{
VertexType adList[MAXVEX]; //声明顶点数组
EdgeType arc[MAXVEX][MAXVEX]; //声明权值数组
int numVerexes,numEdges; //顶点数,边数
}Graph; //邻接表结构体
typedef struct EdgeNode //边表结构体
{
int adjSub; //存储边表的下标
// EdgeType weight; //权重
struct EdgeNode *next; //指向下一个相邻顶点的指针
}EdgeNode; typedef struct VertexNode //顶点结构体
{
VertexType date; //顶点内容
EdgeNode *firstEdge; //指向顶点第一个邻接点
}GraphList[MAXVEX]; typedef struct //邻接表结构体
{
GraphList graphList; //顶点数组
int numVerexes, numEdges; //顶点数,边数
}AjaGraph; //十字链表结构体
typedef struct Orthogonal //边表结构体
{
int tailVex; //当前顶点下标
int headVex; //弧尾顶点下标
struct Orthogonal *tailLink; //指向入度弧的弧尾
struct Orthogonal *headLink; //指向出度弧的弧头
}Orthogonal; typedef struct //顶点结构体
{
VertexType date; //顶点内容
Orthogonal *firstin; //指向第一个弧头为自己的点
Orthogonal *firstout; //指向第一个弧尾为自己的点
}Orthogonal_Node[MAXVEX]; typedef struct
{
Orthogonal_Node orth_Node; //声明结点数组
int numVertexes,numEdges; //顶点数量,边数量
}OrthGraph; //十字链表图结构 //边集数组结构体
typedef struct //边结构体
{
int iVex; //顶点位置
int jVex; //顶点位置
EdgeType weight; //权重
}EdgeArray[MAXVEX]; typedef struct //图结构体
{
VertexType VexterList[MAXVEX]; //顶点数组
EdgeArray EdgeList; //边数组
int numVexteres, numEdges; //顶点数量,边数量
}EdgeListArray; //边集数组图结构 //邻接多重表
typedef struct EdgeList_multiple //边表结点
{
int iVex; //当前顶点下标
int jVex; //终点下表
struct EdgeList_multiple *iLink; //指向与顶点i有相同起点的结点
struct EdgeList_multiple *jLink; //指向与顶点j有相同终点的结点
}EdgeList_multiple; typedef struct
{
VertexType date; //顶点数据
EdgeList_multiple *firstEdge; //指向顶点的第一个邻接点
}Vexs; typedef struct
{
Vexs vexs[MAXVEX]; //建立顶点数组
int numVexes; //顶点数量
int numEdges; //边的数量
}MultipleGraph; //邻接多重表创建图结构
int CreatGraphMultiple(MultipleGraph *G)
{
int i,j,k;
printf("请输入顶点数i与边的数量j:");
scanf("%d,%d",&G->numVexes,&G->numEdges);
EdgeList_multiple *em;
getchar();
for(i = ; i < G->numVexes; i++)
{
printf("请输入第%d个顶点:",i);
scanf("%c", &G->vexs[i].date);
getchar();
}
for(k = ; k < G->numEdges; k++)
{
printf("请输入边的起点i与终点j的下标:");
scanf("%d,%d",&i,&j);
em = (EdgeList_multiple *)malloc(sizeof(EdgeList_multiple)); //创建新结点空间
em->iVex = i;
em->jVex = j;
em->iLink = G->vexs[i].firstEdge;
G->vexs[i].firstEdge = em;
em->jLink = G->vexs[j].firstEdge;
G->vexs[j].firstEdge = em;
}
return ;
} //邻接矩阵创建图结构
int CreatGraph(Graph *G)
{
int i,j,k,w;
printf("请输入结点数i,边数j:");
scanf("%d,%d",&G->numVerexes,&G->numEdges); //写入顶点数和边数
for(i = ; i < G->numVerexes; i++) //初始化顶点数组
{
printf("请输入第%d个顶点:",i);
scanf("%c",&G->adList[i]);
getchar();
}
for(i = ; i < G->numVerexes; i++) //初始化权值矩阵
for(j = ; j < G->numVerexes; j++)
G->arc[i][j] = INFINITY;
for(k = ; k<G->numEdges; k++) //写入权值
{
printf("请输入需要添加权值的下标i和下标j,及其权值w:");
scanf("%d,%d,%d", &i, &j, &w);
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j]; //无向图的对称性
}
return ;
} //邻接表创建图结构
int CreatAjaGraph(AjaGraph *G)
{
int i, j, k;
char a;
EdgeNode *e; //声明边表新结点
printf("请输入顶点i和边数j:");
scanf("%d, %d", &G->numVerexes, &G->numEdges); //写入顶点数与边数
getchar();
for(i = ; i < G->numVerexes; i++) //初始化顶点数组
{
printf("请输入第%d个结点:",i);
scanf("%c",&G->graphList[i].date);
getchar();
printf("%c\n", a);
G->graphList[i].firstEdge = NULL; //顶点数组的指针域指向空
}
for(k = ; k < G->numEdges; k++) //构建边表
{
printf("请输入邻接点Vi与Vj的下标:");
scanf("%d,%d", &i, &j);
e = (EdgeNode *)malloc(sizeof(EdgeNode)); //创建新结点空间
e->adjSub = j; //新结点的数据域为j
e->next = G->graphList[i].firstEdge; //新结点指针域指向顶点指针域
G->graphList[i].firstEdge = e; //顶点指针域指向新结点 e = (EdgeNode *)malloc(sizeof(EdgeNode)); //因为是无向图
e->adjSub = i; //同时为i操作
e->next = G->graphList[j].firstEdge;
G->graphList[j].firstEdge = e;
} return ;
} //十字链表结构创建图结构
int CreatOrthGraph(OrthGraph *G)
{
int i,j,k;
Orthogonal *e;
printf("请输入顶点数量i和边数量j:");
scanf("%d,%d", &G->numVertexes, &G->numEdges); //写入顶点数和边数
for(i = ; i < G->numVertexes; i++) //对顶点数组初始化
{
printf("请输入第%d个结点:",i);
scanf("%c",&G->orth_Node[i].date); //输入顶点内容
getchar();
G->orth_Node[i].firstin = NULL; //将入边表指向空
G->orth_Node[i].firstout = NULL; //将出边表指向空
}
for(k = ; k < G->numEdges; k++) //构建边表
{
printf("请输入起点i与终点j的下标:");
scanf("%d,%d", &i, &j);
e = (Orthogonal *)malloc(sizeof(Orthogonal)); //创建新结点空间
e->tailVex = i; //当前结点等于i
e->headVex = j; //弧尾等于j
e->tailLink = G->orth_Node[i].firstout; //入度指针等于顶点入度指针
G->orth_Node[i].firstout = e; //顶点位置i的firstout指向e
e->headLink = G->orth_Node[j].firstin; //出度指针等于顶点出度指针
G->orth_Node[j].firstin = e; //顶点位置j的firstout指向e
}
return ;
} //边集数组创建图结构
int CreatGraph(EdgeListArray *G)
{
int i, j, w;
printf("请输入顶点数量i与边的数量j:");
scanf("%d,%d", &G->numVexteres, &G->numEdges); //写入顶点数量与边数量
for(i = ; i < G->numVexteres; i++) //构建顶点数组
{
printf("请输入第%d个结点:",i);
scanf("%c",&G->VexterList[i]);
}
for(i = ; i < G->numEdges; i++) //构建边数组
{
printf("请输入顶点i与顶点j及其权重w:");
scanf("%d,%d", &i, &j, &w);
G->EdgeList[i].iVex = i; //这里的i只是边数组的下标,与顶点数组无关
G->EdgeList[i].jVex = j;
G->EdgeList[i].weight = w;
}
return ;
} //遍历邻接矩阵结构
void PrintfGraph(Graph G)
{
int i,j;
for(i = ; i < G.numVerexes; i++)
for(j = ; j < G.numVerexes; j++)
if(G.arc[i][j] != INFINITY && i < j)
printf("顶点: %d , %d, 权重: %d\n", i, j, G.arc[i][j]);
} //邻接表深度优先遍历
void PrintDeepthAjaGraph(AjaGraph G, int i) //递归函数
{
visited[i] = TURE; //将此顶点记为访问过
printf("%c\n", G.graphList[i].date); //打印当前顶点
EdgeNode *ag; //创建顶点指针
ag = G.graphList[i].firstEdge; //将此指针赋值为当前顶点的边表第一个结点
while(ag) //只要ag不为空
{
if(!visited[ag->adjSub]) //如果当前边表第一个结点不为空
PrintDeepthAjaGraph(G, ag->adjSub); //递归
ag = ag->next; //否则ag赋值为ag的下一临结点
}
} void Depth_first(AjaGraph G) //深度优先遍历函数
{
int j;
for(j = ; j < G.numVerexes; j++) //初始化记录数组
visited[j] = FALSE;
for(j = ; j < G.numVerexes; j++) //遍历顶点数组中的每一个顶点
{
// printf("当前结点是:%d, 其是否遍历过 %d\n", j, visited[j]);
if(!visited[j]) //如果当前结点没被访问过
PrintDeepthAjaGraph(G, j); //调用递归函数
}
} //邻接表广度优先搜索
void BFs(AjaGraph G)
{
int j;
SqQueue Q; //创建队Q
InitQue(&Q); //初始化队列
for(j = ; j < G.numVerexes; j++) //初始化记录数组
visited[j] = FALSE;
EdgeNode *ag; //创建边表指针
visited[] = TURE; //将第一个顶点记为访问过
printf("%c\n", G.graphList[].date); //打印第一个顶点
EnQueue(&Q, ); //将第一个顶点入队
while(Q.front != Q.rear) //只要队列不为空
{
DeQueue(&Q, &j); //将当前顶点出队
ag = G.graphList[j].firstEdge; //ag赋值为当前结点的第一个边表结点
while(ag && !visited[ag->adjSub]) //ag不为空且ag未被访问过
{
visited[ag->adjSub] = TURE; //将ag记为访问过
printf("%c\n", G.graphList[ag->adjSub].date); //打印ag
EnQueue(&Q, ag->adjSub); //将ag入队
ag = ag->next; //ag赋值为ag的下一邻接表结点
}
}
} void main()
{
Graph G1;
AjaGraph G2;
OrthGraph G3;
MultipleGraph G4;
EdgeListArray G5;
while(true)
{
int flag = ;
printf("请选择对图的操作:\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("9.退出\n");
int a;
scanf("%d", &a);
switch(a)
{
case :
flag = ;
flag = CreatGraph(&G1);
if(flag)
printf("创建成功\n");
else
printf("创建失败\n");
break;
case :
flag = ;
flag = CreatAjaGraph(&G2);
if(flag)
printf("创建成功\n");
else
printf("创建失败\n");
break;
case :
flag = ;
flag = CreatOrthGraph(&G3);
if(flag)
printf("创建成功\n");
else
printf("创建失败\n");
break;
case :
flag = ;
flag = CreatGraphMultiple(&G4);
if(flag)
printf("创建成功\n");
else
printf("创建失败\n");
break;
case :
flag = ;
CreatGraph(&G5);
if(flag)
printf("创建成功\n");
else
printf("创建失败\n");
break;
case :
PrintfGraph(G1);
break;
case :
Depth_first(G2);
break;
case :
BFs(G2);
break;
case :
return;
default:
printf("选择错误\n");
break;
}
}
}
图的存储结构与操作--C语言实现的更多相关文章
- 图的存储结构大赏------数据结构C语言(图)
图的存储结构大赏------数据结构C语言(图) 本次所讲的是常有的四种结构: 邻接矩阵 邻接表 十字链表 邻接多重表 邻接矩阵 概念 两个数组,一个表示顶点的信息,一个用来表示关联的关系. 如果是无 ...
- C++编程练习(9)----“图的存储结构以及图的遍历“(邻接矩阵、深度优先遍历、广度优先遍历)
图的存储结构 1)邻接矩阵 用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中边或弧的信息. 2)邻接表 3)十字链表 4)邻接多重表 5)边集数组 本文只用代码实现用 ...
- 图的存储结构(邻接矩阵与邻接表)及其C++实现
一.图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为: G=(V,E) 其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合. 注: 在线性表中,元素个数可以为零, ...
- 【algo&ds】6.图及其存储结构、遍历
1.什么是图 图表示"多对多"的关系 包含 一组顶点:通常用 V(Vertex)表示顶点集合 一组边:通常用 E(Edge)表示边的集合 边是顶点对:(v,w)∈ E,其中 v,w ...
- 【PHP数据结构】图的存储结构
图的概念介绍得差不多了,大家可以消化消化再继续学习后面的内容.如果没有什么问题的话,我们就继续学习接下来的内容.当然,这还不是最麻烦的地方,因为今天我们只是介绍图的存储结构而已. 图的顺序存储结构:邻 ...
- K:图的存储结构
常用的图的存储结构主要有两种,一种是采用数组链表(邻接表)的方式,一种是采用邻接矩阵的方式.当然,图也可以采用十字链表或者边集数组的方式来进行表示,但由于不常用,为此,本博文不对其进行介绍. 邻接 ...
- 图的存储结构:邻接矩阵(邻接表)&链式前向星
[概念]疏松图&稠密图: 疏松图指,点连接的边不多的图,反之(点连接的边多)则为稠密图. Tips:邻接矩阵与邻接表相比,疏松图多用邻接表,稠密图多用邻接矩阵. 邻接矩阵: 开一个二维数组gr ...
- 图的存储结构的实现(C/C++实现)
存档: #include <stdio.h> #include <stdlib.h> #define maxv 10 #define max 10 typedef char e ...
- Java数据结构系列之——栈(2):栈的链式存储结构及其操作
package Stack; import LinkList.SinglyLinkList; public class LinkListStack { private SinglyLinkList&l ...
随机推荐
- [Pandas]利用Pandas处理excel数据
Python 处理excel的第三包有很多,比如XlsxWriter.xlrd&xlwt.OpenPyXL.Microsoft Excel API等,最后综合考虑选用了Pandas. Pand ...
- Project Euler 57: Square root convergents
五十七.平方根收敛(Square root convergents) 二的平方根可以表示为以下这个无穷连分数: \[ \sqrt 2 =1+ \frac 1 {2+ \frac 1 {2 +\frac ...
- [转载]2.6 UiPath循环嵌套的介绍和使用
一.循环嵌套的介绍 一个循环体内又包含另一个完整的循环结构,就称之为循环嵌套.内嵌的循环中还可以嵌套循环,这就是多层循环,也叫做多重循环. 二.在UiPath中结合使用循环嵌套生成99乘法表 1.打开 ...
- windows备份mysql数据库
1.编写执行文件mysql_backup.bat rem auther:ELSON ZENGrem date:20191104rem mysql backup! @echo off set mysql ...
- C# Web分页功能实现
无论是网站还是APP分页功能都是必不可少的.为什么使用分页呢? 1,加载速度快,不会占用服务器太多资源,减少服务器压力. 2,减少数据库压力. 3,提升用户体验. 那么我们常用的分页方法有两种. 1, ...
- ES6学习 let const
1.前言 发现网易云笔记 单纯的记笔记没什么意思,所以今天来博客园写学习感受了,毕设做了休息时间就来写写新学的知识 哈哈哈 !! 2.ES6 就是JavaScript 语言的下一代标准,2015年6月 ...
- 高质量App的架构设计与思考!
最近在做一功能不大.业务也不复杂的小众App,以往做App是发现自己从来没有考虑过一些架构方面的问题,只是按照自己以往的习惯去写代码,忽略了App的设计.本次分享主要包含一些开发App的小经验和技巧, ...
- bash:echo
echo 'xxxx'自带换行 echo -n ‘xxxxxx’ 取消换行 echo -e "xxxxxxxxxxxx"允许转义字符(两种引号对转以字符效果相同,影响$变量) 转义 ...
- Extjs导入Excel文件之后grid自动刷新显示刚插入的数据
var winUpload = new Ext.Window({ title: '导入excel文件', width: 400, height:200, listeners: { close: fun ...
- 关于log4j
搭建完maven+SpringMVC以后,我们还需要添加日志功能,方便记录应用在应用中产生的日志信息. 1.添加依赖包:依赖中除了对slf4j依赖外还有对邮件插件的依赖.当遇到报错的时候可以发送邮件到 ...