图的存储结构与操作--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 ...
随机推荐
- Bootstrap布局基础
1.栅格系统(布局)Bootstrap内置了一套响应式.移动设备优先的流式栅格系统,随着屏幕设备或视口(viewport)尺寸的增加,系统会自动分为最多12列. 我在这里是把Bootstrap中的栅 ...
- 前端组件用 Scope 发布 npm 包的方法
1.引言 多人.多组织或多组件发布 npm 包到同一个仓库时,可能出现命名冲突问题. 为了解决这个问题,npm 引入了“scope”(范围)概念. 在 Angular 项目中,我们通常可以看到“@an ...
- python机器学习——感知器
最近在看机器学习相关的书籍,顺便把每天阅读的部分写出来和大家分享,共同学习探讨一起进步!作为机器学习的第一篇博客,我准备从感知器开始,之后会慢慢更新其他内容. 在实现感知器算法前,我们需要先了解一下神 ...
- P1041 传染病控制(noip2003)(搜索)
呃呃呃...真的是惨烈啊... 今天的模拟赛是真的惨..... 本题,正解居然是搜索!!!!!! 蒟蒻自己歪歪了一个貌似是正解但是却连一半都没过的错解. 先解释一下自己的dp思路把. $f[i][u] ...
- m101 真*sb($\huge 全场最瞎$)
不想说这场考试,T1全场切,但是我: T3全场30,但是: 鬼知道我为什么敲的是p*p啊(而且还炸精了!) kuku----! $\huge 全场最瞎$
- Ansible之入门简介
一.ansible简介 ansible是一款由python语言编写的一款自动化运维工具,它集合了众多运维工具(puppet.cfengine.chef.func.fabric)优点,实现了批量系统配置 ...
- 2019CSP day1t1 格雷码
题目描述 通常,人们习惯将所有 \(n\) 位二进制串按照字典序排列,例如所有 \(2\) 位二进制串按字典序从小到大排列为:\(00,01,11,10\). 格雷码(\(Gray Code\))是一 ...
- Python多线程与队列
Python多线程与Queue队列多线程在感官上类似于同时执行多个程序,虽然由于GIL的存在,在Python中无法实现线程的真正并行,但是对于某些场景,多线程仍不失为一个有效的处理方法: 1,不紧急的 ...
- Pashmak and Parmida's problem(树状数组)
题目链接:http://codeforces.com/contest/459/problem/D 题意: 数列A, ai表示 i-th 的值, f(i,j, x) 表示[i,j]之间x的数目, 问:当 ...
- MATLAB相机标定转XMl代码
function writeXML(cameraParams,file) %writeXML(cameraParams,file) %功能:将相机校正的参数保存为xml文件 %输入: %cameraP ...