数据结构之图的数组表示法

来源:互联网 发布:网络金融诈骗公司名单 编辑:程序博客网 时间:2024/04/25 21:26

        

        图( Graph )是 由顶点的有穷非空集合和顶点之间边的集合组成,通常表示示为: G (V, E ) ,其中, G表示一个图, V 是图 G 中顶点的集合, E 是 图 G 中边的集合 。

       图状结构是一种比树形结构更复杂的非线性结构。在树状结构中,结点间具有分支层次关系,每一层上的结点只能和上一层中的至多一个结点相关,但可能和下一层的多个结点相关.

                                   


        上面是图的概念,看不懂是吧,其实我也看不懂,呵呵。其实这个图的相关概念也没有那么玄乎,建议找找离散数学的书看看,如果没有相关的基础是很难理解图这个坑爹的玩意的,光一堆术语就能把人搞的晕头转向的,更别说代码中指针还指来指去的(如果实在没时间可以看看这个链接)。


     数据结构中图的基本操作如下:

(1) CreatGraph(G)输入图G 的顶点和边,建立图G 的存储。(2)DestroyGraph(G)释放图G 占用的存储空间。(3)GetVex(G,v)在图G 中找到顶点v,并返回顶点v 的相关信息。(4)PutVex(G,v,value)在图G 中找到顶点v,并将value 值赋给顶点v。(5)InsertVex(G,v)在图G 中增添新顶点v。(6)DeleteVex(G,v)在图G 中,删除顶点v 以及所有和顶点v 相关联的边或弧。(7)InsertArc(G,v,w)在图G 中增添一条从顶点v 到顶点w 的边或弧。(8)DeleteArc(G,v,w)在图G 中删除一条从顶点v 到顶点w 的边或弧。(9)DFSTraverse(G,v)在图G 中,从顶点v 出发深度优先遍历图G。(10)BFSTtaverse(G,v)在图G 中,从顶点v 出发广度优先遍历图G。在一个图中,顶点是没有先后次序的,但当采用某一种确定的存储方式存储后,存储结构中顶点的存储次序构成了顶点之间的相对次序,这里用顶点在图中的位置表示该顶点的存储顺序;同样的道理,对一个顶点的所有邻接点,采用该顶点的第i 个邻接点表示与该顶点相邻接的某个顶点的存储顺序,在这种意义下,图的基本操作还有:(11)LocateVex(G,u)在图G 中找到顶点u,返回该顶点在图中位置。(12)FirstAdjVex(G,v)在图G 中,返回v 的第一个邻接点。若顶点在G 中没有邻接顶点,则返回“空”。(13)NextAdjVex(G,v,w)在图G 中,返回v 的(相对于w 的)下一个邻接顶点。若w 是v 的最后一个邻接点,则返回“空”。



          图的存储结构有多种:数组表示法,邻接表,十字链表法,多重链表法,为了熟悉图的运用我把每种表示方法都实现了一遍,有兴趣的可以看看我后面更新的文章。当然这篇文章主要介绍数组表示法,下面介绍数组表示法。


        首先表示图要就要表示图中顶点的信息又要表示顶点间的关系,考虑到图是由顶点和边或弧两部分组成。合在一起比较困难,那就很自然地考虑到分两个结构来分别存储。顶点不分大小、主次,所以用一个一维数组来存储是很不错的选择。而边或弧由于是顶点与顶点之间的关系, 一维搞不定,那就考虑用 一个二维数组来存储。于是我们的邻接矩阵的方案就诞生了。图的邻接矩阵 ( Adjacency Matrix) 存储方式是用两个数组来表示圈。一个一维数组存储圈中顶点信息 ,一个二维数组〈称为邻接矩阵)存储图中的边或弧的信息。


设图G有n个顶点,则邻接顶点是一个n * n的矩阵,具体定义如下(下面的是不带权值的):

                           

比较抽象,那么看两个例子:

                                                       



看完不带权的再看带权的:

                                       


                              



大概看明白了,那么我们来看数组表示法的数据存储结构:

#define INFINITY 65535 //表示无穷大-->在带权的图中用到,即网#define MAX_VERTEX_NUM 20 //图的最大定点数#define MAX_INFO 20  //每条弧附带信息最大长度//顶点关系类型typedef int VRType;//附加信息类型typedef char InfoType;//顶点数据类型typedef int VertexType;//图的种类:分别代表有向图,有向网,无向图,无向网typedef enum {DG,DN,UDG,UDN} GraphKind;typedef struct {VRType adj; //定点关系类型,对无权图用1或0表示是否相邻;对带权图,则为权值类型InfoType *info;//附加信息指针}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];typedef struct {//顶点向量VertexType vexs[MAX_VERTEX_NUM];//邻接矩阵AdjMatrix arcs;//图的当前顶点数int vexnum;//图的弧数int arcnum;//图的种类GraphKind kind;}MGraph;


上程序源码:

头文件mgraph.h

/*---------------------------------------------------------------------------------* file:mgraph.h* date:10-9-2014* author:doodlesomething@163.com* version:1.0* description:图的数组表示及基本操作------------------------------------------------------------------------------------*/#define TRUE 1#define ERROR 0#define OK 1#define FALSE 0#define INFINITY 65535 //表示无穷大-->在带权的图中用到,即网#define MAX_VERTEX_NUM 20 //图的最大定点数#define MAX_INFO 20  //每条弧附带信息最大长度typedef int Status;//顶点关系类型typedef int VRType;//附加信息类型typedef char InfoType;//顶点数据类型typedef int VertexType;//图的种类:分别代表有向图,有向网,无向图,无向网typedef enum {DG,DN,UDG,UDN} GraphKind;typedef struct {VRType adj; //定点关系类型,对无权图用1或0表示是否相邻;对带权图,则为权值类型InfoType *info;//附加信息指针}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];typedef struct {//顶点向量VertexType vexs[MAX_VERTEX_NUM];//邻接矩阵AdjMatrix arcs;//图的当前顶点数int vexnum;//图的弧数int arcnum;//图的种类GraphKind kind;}MGraph;//数组标记某元素是否被访问过int visited[MAX_VERTEX_NUM];//创建图,包括有无向图,有无向网Status CreateGraph(MGraph *G);//创建无向图Status CreateUDG(MGraph *G);//创建有向图Status CreateDG(MGraph *G);//创建无向网Status CreateUDN(MGraph *G);//创建有向网Status CreateDN(MGraph *G);//返回某顶点在图中的位置int LocateVex(MGraph G,VertexType v);//打印元素Status PrintElem(VertexType elem);//深度优先遍历图Status DFSTraverse(MGraph G,Status (*Visit) (VertexType));//深度优先递归遍历void DFS(MGraph G,int i,Status (*Visit) (VertexType));//返回v的值VertexType GetVex(MGraph G,int v);//对v进行赋值Status PutVex(MGraph *G,int v,VertexType value);//返回v的第一个邻接顶点int FirstAdjVex(MGraph G,int v);//w是v的邻接点,返回v相对于w的下一个邻接点int NextAdjVex(MGraph G,int v,int w);//插入一个顶点,但是不进行插入弧Status InsertVex(MGraph *G,VertexType v);//删除一个顶点和相关的弧Status DeleVex(MGraph *G,VertexType v);//在顶点v,w间插入一段弧或边Status InsertArc(MGraph *G,VertexType v,VertexType w);//删除一段弧或边,但是不删除顶点Status DeleArc(MGraph *G,VertexType v,VertexType w);




实现具体文件MGraph.c


/*---------------------------------------------------------------------------------* file:MGraph.c* date:10-9-2014* author:doodlesomething@163.com* version:1.0* description:图的数组表示及基本操作------------------------------------------------------------------------------------*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include "mgraph.h"#include "linkqueue.h"/** @description:创建图,包括有向图,无向图,有向网,无向网*/Status CreateGraph(MGraph *G) {printf("please enter the kind of the graph(DG:0,DN:1,UDG:2,UDN:3):");scanf("%d",&(*G).kind);switch((*G).kind) {case UDG:return CreateUDG(G);break;case DG:return CreateDG(G);case UDN:return CreateUDN(G);break;case DN:return CreateDN(G);break;default:return ERROR;}}/** @description:创建无向图* @more:分几步来做1.确定顶点数/弧数2.确定各个顶点的值3.初始化邻接矩阵4.确定邻接矩阵*/Status CreateUDG(MGraph *G) {int i,j,k,infoflag,len;char c;//设置一个暂存区和一个临时指针char str[MAX_INFO];char *info;VertexType v1,v2;len = 0;//确定顶点数/弧数printf("please enter vexnum, arcnum is info(1 or 0):");scanf("%d,%d,%d",&(*G).vexnum,&(*G).arcnum,&infoflag);//确定各个顶点的值printf("the value of each vertex:");for(i = 0;i < (*G).vexnum ; i++)scanf("%d,",&(*G).vexs[i]);//初始化邻接矩阵for(i = 0; i < (*G).vexnum; i++) for(j = 0;j < (*G).vexnum ; j++) {(*G).arcs[i][j].adj = 0;//无向图(*G).arcs[i][j].info = NULL;}//确定邻接矩阵printf("please %d heads and %d tails:\n",(*G).vexnum,(*G).arcnum);for(k = 0; k < (*G).arcnum; k++) {scanf("%d,%d",&v1,&v2);i = LocateVex(*G,v1);j = LocateVex(*G,v2);if(i >= 0 && j >= 0)(*G).arcs[i][j].adj = (*G).arcs[j][i].adj = 1;//无向图,对称矩阵//如果顶点有附带信息,则输入并申请空间if(infoflag) {printf("please enter the info:");while( (c = getchar()) != '#')str[len++] = c;info = (char *) malloc(len * sizeof(char));str[len] = '\0';strcpy(info,str);(*G).arcs[i][j].info = (*G).arcs[j][i].info = info;}}(*G).kind = UDG;return OK;}/** @description:创建有向图* @more:分几步来做1.确定顶点数/弧数2.确定各个顶点的值3.初始化邻接矩阵4.确定邻接矩阵*/Status CreateDG(MGraph *G) {int i,j,k,len,infoflag;VertexType v1,v2;char str[MAX_INFO];char *info;char c;//确定顶点数/弧数printf("please enter vexnum , arcnum and is info(1 or 0):");scanf("%d,%d,%d",&(*G).vexnum,&(*G).arcnum,&infoflag);//确定各个顶点的值printf("the value of each vertex:");for(i = 0;i < (*G).vexnum ; i++)scanf("%d,",&(*G).vexs[i]);//初始化邻接矩阵for(i = 0; i < (*G).vexnum; i++) for(j = 0;j < (*G).vexnum ; j++) {(*G).arcs[i][j].adj = 0;//有向图(*G).arcs[i][j].info = NULL;}//确定邻接矩阵printf("please %d heads and %d tails:\n",(*G).vexnum,(*G).arcnum);for(k = 0; k < (*G).arcnum; k++) {scanf("%d,%d",&v1,&v2);i = LocateVex(*G,v1);j = LocateVex(*G,v2);if(i >= 0 && j >= 0)(*G).arcs[i][j].adj = 1;//有向图//如果顶点有附带信息,则输入并申请空间if(infoflag) {printf("please enter the info:");while( (c = getchar()) != '#')str[len++] = c;info = (char *) malloc(len * sizeof(char));strcpy(info,str);(*G).arcs[i][j].info = info;}}(*G).kind = DG;return OK;}/** @description:创建有向网*/Status CreateDN(MGraph *G) {int i,j,k,len,infoflag,w;VertexType v1,v2;char str[MAX_INFO];char *info;char c;//确定顶点数/弧数printf("please enter vexnum , arcnum and is info(1 or 0):");scanf("%d,%d,%d",&(*G).vexnum,&(*G).arcnum,&infoflag);//确定各个顶点的值printf("the value of each vertex:");for(i = 0;i < (*G).vexnum ; i++)scanf("%d,",&(*G).vexs[i]);//初始化邻接矩阵for(i = 0; i < (*G).vexnum; i++) for(j = 0;j < (*G).vexnum ; j++) {(*G).arcs[i][j].adj = INFINITY;//有向网(*G).arcs[i][j].info = NULL;}//确定邻接矩阵printf("please %d heads and %d tails and weights:\n",(*G).vexnum,(*G).arcnum);for(k = 0; k < (*G).arcnum; k++) {scanf("%d,%d,%d",&v1,&v2,&w);i = LocateVex(*G,v1);j = LocateVex(*G,v2);if(i >= 0 && j >= 0)(*G).arcs[i][j].adj = w;//有向图//如果顶点有附带信息,则输入并申请空间if(infoflag) {printf("please enter the info:");while( (c = getchar()) != '#')str[len++] = c;info = (char *) malloc(len * sizeof(char));strcpy(info,str);(*G).arcs[i][j].info = info;}}(*G).kind = DN;return OK;}/** @description:创建无向网*/Status CreateUDN(MGraph *G) {int i,j,k,len,infoflag,w;VertexType v1,v2;char str[MAX_INFO];char *info;char c;//确定顶点数/弧数printf("please enter vexnum , arcnum and is info(1 or 0):");scanf("%d,%d,%d",&(*G).vexnum,&(*G).arcnum,&infoflag);//确定各个顶点的值printf("the value of each vertex:");for(i = 0;i < (*G).vexnum ; i++)scanf("%d,",&(*G).vexs[i]);//初始化邻接矩阵for(i = 0; i < (*G).vexnum; i++) for(j = 0;j < (*G).vexnum ; j++) {(*G).arcs[i][j].adj = INFINITY;//无向网(*G).arcs[i][j].info = NULL;}//确定邻接矩阵printf("please heads,tails and weights:\n");for(k = 0; k < (*G).arcnum; k++) {scanf("%d,%d,%d",&v1,&v2,&w);i = LocateVex(*G,v1);j = LocateVex(*G,v2);if(i >= 0 && j >= 0)(*G).arcs[i][j].adj = (*G).arcs[j][i].adj = w;//无向网//如果顶点有附带信息,则输入并申请空间if(infoflag) {printf("please enter the info:");while( (c = getchar()) != '#')str[len++] = c;info = (char *) malloc(len * sizeof(char));strcpy(info,str);(*G).arcs[i][j].info = info;}}(*G).kind = UDN;return OK;}/** @description:判断图中是否存在v顶点,存在则返回该顶点在图中的位置,否则返回其他信息*/int LocateVex(MGraph G,VertexType v) {int i;for(i = 0;i < G.vexnum; i++)//匹配则返回if(G.vexs[i] == v)return i;return -1;}/** @description:深度优先遍历图* @more:分为两步1.一定要重新初始化访问记录数组2.访问没有访问过的顶点,并更新访问记录数组* @本函数中的第二个循环主要是为了保证每个顶点都能被访问到   而DFS中的循环是在寻找当前节点的相邻节点来访问   关键的关键在于递归的理解-->废话*/Status DFSTraverse(MGraph G,Status (*Visit) (VertexType)) {int i;//初始化访问记录数组for(i = 0;i < G.vexnum; i++)visited[i] = FALSE;for(i = 0;i < G.vexnum ; i++) if(!visited[i])DFS(G,i,Visit);return OK;}/** @description:深度优先递归遍历*/void DFS(MGraph G,int i,Status (*Visit) (VertexType)) {int j,w;//标记visited[i] = TRUE;Visit(G.vexs[i]);for(w = FirstAdjVex(G,i); w >= 0; w = NextAdjVex(G,i,w))if(!visited[w])DFS(G,w,Visit);}/** @description:图的广度优先遍历* @more:值得注意的是广度遍历和树中的层序遍历采用的是同样的思路-->采用队列辅助*/Status BFSTraverse(MGraph G,Status (*Visit)(VertexType)) {LinkQueue Q;int i,w,u;//必须重新初始化访问标记数组for(i = 0;i < G.vexnum; i++) visited[i] = FALSE;InitQueue(&Q);//用于确保每个顶点都会被访问到for(i = 0;i < G.vexnum; i++) {if(!visited[i]) {Visit(G.vexs[i]);visited[i] = TRUE;//进队EnQueue(&Q,i);while(!QueueEmpty(Q)) {//出队一个元素DeQueue(&Q,&u);for(w = FirstAdjVex(G,u); w >= 0; w = NextAdjVex(G,u,w)) {if(!visited[w]) {visited[w] = TRUE;Visit(G.vexs[w]);EnQueue(&Q,w);}}}}}return OK;}/** @description:返回v的值*/VertexType GetVex(MGraph G,int v) {if(v >= G.vexnum || v < 0)exit(ERROR);return G.vexs[v];}/** @description:对v进行赋值*/Status PutVex(MGraph *G,int v,VertexType value) {if(v >= (*G).vexnum || v < 0)exit(ERROR);(*G).vexs[v] = value;return OK;}/** @description:返回v(序号)的第一个相邻节点(序号)*/int FirstAdjVex(MGraph G,int v) {if(v > G.vexnum || v < 0)return -1;int i,j;j = 0;//如果是网if(G.kind == DN || G.kind == UDN )j = INFINITY;for(i = 0;i < G.vexnum; i++) if(G.arcs[v][i].adj != j)return  i;return -1;}/** @description:w是v的相邻节点,返回v相对w的下一个节点的序号,否则返回-1*/int NextAdjVex(MGraph G,int v,int w) {int i,j;j = 0;//如果为网if(G.kind == DN || G.kind == UDN)j = INFINITY;//两顶点不相邻if(G.arcs[v][w].adj == j)return -1;//从w之后的节点开始就可以for(i = w + 1; i < G.vexnum; i++) if(G.arcs[v][i].adj != j)return i;return -1;}/** description:插入一个顶点,但是进行弧的插入,交给后面的插入弧操作进行* @more:注意需要同时对邻接矩阵行和列的跟新信息,还有这里的插入是直接在的位置进行插入,不指定位置*/Status InsertVex(MGraph *G,VertexType v) {int i;//进行赋值(*G).vexs[(*G).vexnum] = v;if((*G).kind == DN || (*G).kind == UDN)for(i = 0; i <= (*G).vexnum; i++) {//对行的邻接关系进行初始化(*G).arcs[(*G).vexnum][i].adj = INFINITY;//对列的邻接关系进行初始化(*G).arcs[i][(*G).vexnum].adj = INFINITY;}else for(i = 0; i <= (*G).vexnum; i++) {//对行的邻接关系进行初始化(*G).arcs[(*G).vexnum][i].adj = 0;//对列的邻接关系进行初始化(*G).arcs[i][(*G).vexnum].adj = 0;}//对行进行初始化(*G).arcs[(*G).vexnum][i].info = NULL;//对列进行初始化(*G).arcs[i][(*G).vexnum].info = NULL;//图的顶点数目加一(*G).vexnum++;return OK;}/** @description:删除G中的顶点v其相关的弧(难点在于弧的删除)*/Status DeleVex(MGraph *G,VertexType v) {int k,i,j;VRType m;m = 0;k = LocateVex(*G,v);if(k >= (*G).vexnum || k < 0)return ERROR;//图的类型为网if((*G).kind == DN || (*G).kind == UDN)m = INFINITY;/*这里主要是释放弧的附带信息,如果是有向图则为释放入弧,如果是无向图,由于其是对称矩阵,释放列或行就可*/for(i =0;i < (*G).vexnum; i++)if((*G).arcs[i][k].adj != m) {if((*G).arcs[i][k].info)free((*G).arcs[i][k].info);(*G).arcnum--;}//有向图存在出弧if((*G).kind == DG || (*G).kind == DN) for(i = 0;i < (*G).vexnum; i++)if((*G).arcs[k][i].adj != m) {if((*G).arcs[k][i].info)free((*G).arcs[k][i].info);(*G).arcnum--;}//将顶点数组中的v之后的元素前移for(i = k + 1; i < (*G).vexnum; i++) (*G).vexs[i-1] = (*G).vexs[i];//邻接矩阵v之后的前移动for(i = 0;i < (*G).vexnum; i++)for(j = k + 1; j < (*G).vexnum ; j++)(*G).arcs[i][j-1] = (*G).arcs[i][j];//邻接矩阵v之下的上移for(i = 0;i < (*G).vexnum; i++) for(j = k +1; j < (*G).vexnum ; j++)(*G).arcs[j-1][i] = (*G).arcs[j][i];(*G).vexnum--;return OK;}/** @description:增添弧<v,w>,若是无向的,则还增添对称弧<w,v>注意v,w已经存在图中*/Status InsertArc(MGraph *G,VertexType v,VertexType w) {int v1,w1,infoflag,len;char str[MAX_INFO],c,*info;//获取顶点对应位置v1 = LocateVex(*G,v);w1 = LocateVex(*G,w);len = 0;if(v1 < 0 || w1 < 0)return ERROR;if((*G).kind == DN || (*G).kind == UDN) {printf("please enter the weight:");scanf("%d",&(*G).arcs[v1][w1].adj);}else (*G).arcs[v1][w1].adj = 1;printf("please make sure is info(1 or 0):");scanf("%d",&infoflag);//边或弧有附带信息if(infoflag) {while( (c = getchar()) != '#') str[len++] = c;info = (char *) malloc(len * sizeof(char));strcpy(info,str);(*G).arcs[v1][w1].info = info;}//注意如果是无向的,应该添加对称边<w,v)if((*G).kind == UDG || (*G).kind == UDN) {(*G).arcs[w1][v1].adj = (*G).arcs[v1][w1].adj;(*G).arcs[w1][v1].info = (*G).arcs[v1][w1].info;}(*G).arcnum++;return OK;}/** @description:删除弧或边<v,w>,如果图为无向,则同时删除其对称弧或边*/Status DeleArc(MGraph *G,VertexType v,VertexType w) {int v1,w1,m;v1 = LocateVex(*G,v);w1 = LocateVex(*G,w);m = 0;if(v1 < 0 || w1 <0)return ERROR;if((*G).kind == DN || (*G).kind == UDN)m = INFINITY;(*G).arcs[v1][w1].adj = m;if((*G).kind == UDN || (*G).kind == UDG)(*G).arcs[w1][v1].adj = (*G).arcs[v1][w1].adj;if((*G).arcs[v1][w1].info) {free((*G).arcs[v1][w1].info);if((*G).kind == UDN || (*G).kind == UDG)free((*G).arcs[w1][v1].info);}(*G).arcnum--;return OK;}/** @description:打印元素*/Status PrintElem(VertexType elem) {printf("%d",elem);return OK;}





测试文件:test.c

/*---------------------------------------------------------------------------------* file:MGraph.c* date:10-9-2014* author:doodlesomething@163.com* version:1.0* description:图的数组表示及基本操作------------------------------------------------------------------------------------*/#include <stdio.h>#include "mgraph.h"int main(int argc,char *argv[]) {MGraph G;//建图CreateGraph(&G);//深度优先遍历图DFSTraverse(G,PrintElem);printf("\n");BFSTraverse(G,PrintElem);printf("\n");         /*    测试用例结果:    please enter the kind of the graph(DG:0,DN:1,UDG:2,UDN:3):2    please enter vexnum, arcnum is info(1 or 0):8,8,0    the value of each vertex:1,2,3,4,5,6,7,8    please 8 heads and 8 tails:    1,2    1,3    2,4    2,5    3,6    3,7    4,8     5,8    12485367    12345678    */ }




完整的源码可以看:GitHub

 本博客持续更新,欢迎指正,互相学习。


0 0
原创粉丝点击