[数据结构]第七章-图(读书笔记1)

第七章--图

图(Graph)是一种较线性表和树更为复杂的数据结构。对于无向图,e的取值范围是0到1/2n(n-1),有1/2n(n-1)条边的无向图称为完全图(Completed graph)。有很少的边或弧的图称为稀疏图(Sparse graph)。反之称为稠密图(Dense graph)。
有时图的边或弧具有与它相关的数,这种与图的边或弧相关的数叫做权(Weight)。这些权可以表示从一个顶点到另一个顶点的距离或耗费。这种带权的图通常称为网(Network)。

7.2 图的存储结构
图的存储结构有数组表示法和邻接表法。数组表示法是用两个数组分别存储数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。

#define INFINITY INT_MAX	/* 用整型最大值代替∞ */
#define MAX_VERTEX_NUM 20  /* 最大顶点个数 */
#define MAX_NAME 20
#define MAX_INFO 20

typedef enum{
	DG,
	DN,
	AG,
	AN
}GraphKind; /* {有向图,有向网,无向图,无向网} */

typedef int VRType;
typedef char InfoType;
typedef char VertexType[MAX_NAME];
typedef struct ArcCell{
	VRType	adj;		/*VRType是顶点关系类型。对无权图用1或0来表示是否相邻*/
	                    /*对于有权图,则为权值类型*/
	InfoType *info;		/*该弧相关信息的指针*/
}ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];

typedef struct{
	VertexType	vexs[MAX_VERTEX_NUM];	/*顶点向量*/
	AdjMatrix	arcs;					/*邻街矩阵*/
	int			vexnum,arcnum;			/*图的当前顶点数和弧数*/
	GraphKind	kind;					/*图的种类标志*/
}MGraph;

int LocateVex(MGraph G,VertexType u)
{	/* 初始条件:图G存在,u和G中顶点有相同特征 */
	/* 操作结果:若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
	int i;
	for(i = 0; i <= G.vexnum; i++){
		if ( strcmp(u, G.vexs[i]) == 0 ){
			return i;
		}
	}
	return 0;
}

Status CreateFAG(MGraph *G)
{	/* 采用数组(邻接矩阵)表示法,由文件构造没有相关信息的无向图G */
	int i,j,k;
	char filename[13];
	VertexType va,vb;
	FILE *graphlist;

	printf("请输入数据文件名(f7-1.dat):");
	scanf("%s",filename);
	graphlist = fopen(filename, "r");
	/*输入顶点数*/
	fscanf(graphlist,"%d",&(*G).vexnum);
	/*输入弧数*/
	fscanf(graphlist,"%d",&(*G).arcnum);
	for(i=0;i < (*G).vexnum; i++){/* 构造顶点向量 */
		fscanf(graphlist, "%s", (*G).vexs[i]);
	}
	for(i=0;i < (*G).arcnum; i++){/* 构造邻接矩阵 */
		for(j=0; j < (*G).vexnum; ++j){
			(*G).arcs[i][j].adj = 0; /*图*/
			(*G).arcs[i][j].info = NULL; /*没有相关信息*/
		}
	}
	for(k = 0; k < (*G).arcnum; k++){
		fscanf(graphlist, "%s,%s",va,vb);
		i = LocateVex(*G,va);
		j = LocateVex(*G,vb);
		(*G).arcs[i][j].adj = (*G).arcs[j][i].adj = 1;/*无向图*/
	}
	fclose(graphlist);
	(*G).kind = AG;
	return OK;
}

Status CreateDG(MGraph *G)
{	/* 采用数组(邻接矩阵)表示法,构造有向图G */
	int i,j,k,l,IncInfo;
	char s[MAX_INFO],*info;
	VertexType va,vb;

	printf("请输入有向图G的定点数,弧数,弧是否含其它信息(是1,否0)");
	scanf("%d,%d,%d",&(*G).vexnum, &(*G).arcnum, &IncInfo);
	printf("请输入%d个顶点的值(<%d个字符):\n",(*G).vexnum, MAX_INFO);
	for (i = 0; i < (*G).vexnum; ++i){
		scanf("%s",(*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("请输入%d条弧的弧尾 弧头(以空格作为间隔): \n",(*G).arcnum);
	for(k=0; k<(*G).arcnum; ++k){
		scanf("%s%s%*c",va,vb);  /* %*c吃掉回车符 */
		i = LocateVex(*G,va);
		j = LocateVex(*G,vb);
		(*G).arcs[i][j].adj = 1; /* 有向图 */
		if(IncInfo){
			printf("请输入该弧的相关信息(<%d个字符): ",MAX_INFO);
			gets(s);
			l=strlen(s);
			if(l){
				info=(char*)malloc((l+1)*sizeof(char));
				strcpy(info,s);
				(*G).arcs[i][j].info = info; /* 有向 */
			}
		}
	}
	(*G).kind = DG;
	return OK;
}
Status CreateDN(MGraph *G)
{ /* 采用数组(邻接矩阵)表示法,构造有向网G */
	int i,j,k,w,IncInfo;
	char s[MAX_INFO],*info;
	VertexType va,vb;

	printf("请输入有向网的顶点数,弧数,弧是否含其他信息(是:1 否:0):");
	scanf("%d,%d,%d",&(*G).vexnum, &(*G).arcnum, &IncInfo);
	printf("请输入%d个顶点的值(<%d个字符):\n",(*G).vexnum,MAX_NAME);
	for(i = 0; i < (*G).vexnum; ++i){ /* 构造顶点向量 */
		scanf("%s",(*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("请输入%d条弧的弧尾 弧头 权值(以空格作为间隔): \n",(*G).arcnum);
	for(k = 0;k < (*G).arcnum; ++k){
		scanf("%s%s%d%*c",va,vb,&w);  /* %*c吃掉回车符 */
		i = LocateVex(*G, va);
		j = LocateVex(*G, vb);
		(*G).arcs[i][j].adj = w; /* 有向网 */
		if(IncInfo){
			printf("请输入该弧的相关信息(<%d个字符): ",MAX_INFO);
			gets(s);
			w = strlen(s);
			if(w){
				info = (char*)malloc((w + 1) * sizeof(char));
				strcpy(info,s);
				(*G).arcs[i][j].info = info; /* 有向 */
			}	
		}
	}
	(*G).kind = DN;
   return OK;	
}
Status CreateAG(MGraph *G)
{ /* 采用数组(邻接矩阵)表示法,构造无向图G */
	int i,j,k,l,IncInfo;
	char s[MAX_INFO],*info;
	VertexType va,vb;

	printf("请输入无向图G的顶点数,边数,边是否含其它信息(是:1,否:0): ");
	scanf("%d,%d,%d", &(*G).vexnum, &(*G).arcnum, &IncInfo);
	printf("请输入%d个顶点的值(<%d个字符):\n",&(*G).vexnum, MAX_NAME);
	for(i = 0;i < (*G).vexnum; i++){/* 构造顶点向量 */
		scanf("%s",(*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("请输入%d条边的顶点1 顶点2(以空格作为间隔): \n", (*G).arcnum );
	for(k=0;k<(*G).arcnum;++k){
		scanf("%s%s%*c",va,vb);/*%*c吃掉回车符*/
		i = LocateVex((*G),va);
		j = LocateVex((*G),vb);
		(*G).arcs[i][j].adj = (*G).arcs[j][i].adj = 1; /*无向图*/
		if(IncInfo){
			printf("请输入该边的相关信息(<%d个字符): ", MAX_INFO);
			gets(s);
			l = strlen(s);
			if(l){
				info = (char*)malloc((l+1)*sizeof(char));
				strcpy(info,s);
				(*G).arcs[i][j].info = (*G).arcs[j][i].info = info; /*无向*/
			}
		}
	}
	(*G).kind = AG;
	return OK;
}

Status CreateAN(MGraph *G)
{ /* 采用数组(邻接矩阵)表示法,构造无向网G。算法7.2 */
	int i,j,k,w,IncInfo;
	char s[MAX_INFO],*info;
	VertexType va,vb;	
	printf("请输入无向网G的顶点数,边数,边是否含其它信息(是:1,否:0): ");
	scanf("%d,%d,%d",&(*G).vexnum, &(*G).arcnum, &IncInfo);
	printf("请输入%d个顶点的值(<%d个字符):\n",(*G).vexnum,MAX_NAME);
	for (i = 0; i < (*G).vexnum;++i){/* 构造顶点向量 */
		scanf("%s",(*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("请输入%d条边的顶点1 顶点2 权值(以空格作为间隔): \n",(*G).arcnum);
	for (k = 0; k < (*G).vexnum; ++k){
		scanf("%s%s%d%*c",va,vb,&w);/* %*c吃掉回车符 */
		i = LocateVex((*G),va);
		j = LocateVex((*G),vb);
		(*G).arcs[i][j].adj = (*G).arcs[j][i].adj = w;	/*无向*/
		if(IncInfo){
			printf("请输入该边的相关信息(<%d个字符): ",MAX_INFO);
			gets(s);
			w = strlen(s);
			if(w){
				info = (char*)malloc((w+1)*sizeof(char));
				strcpy(info,s);
				(*G).arcs[i][j].info = (*G).arcs[j][i].info = info; /*无向*/
			}
		}
	}
	(*G).kind = AN;
	return OK;
}
Status CreateGraph(MGraph *G)
{ /* 采用数组(邻接矩阵)表示法,构造图G。算法7.1 */
	printf("请输入图G的类型(有向图:0,有向网:1,无向图:2,无向网:3): ");
	scanf("%d",&(*G).kind);
	switch((*G).kind){
		case DG: return CreateDG(G); /* 构造有向图 */
		case DN: return CreateDN(G); /* 构造有向网 */
		case AG: return CreateAG(G); /* 构造无向图 */
		case AN: return CreateAN(G); /* 构造无向网 */
		default: return ERROR;
   }
}
void DestroyGraph(MGraph *G)
{ /* 初始条件: 图G存在。操作结果: 销毁图G */
	int i,j;
	if ((*G).kind < 2){/*有向*/
		for(i = 0; i < (*G).vexnum; ++i){
			for (j = 0; j < (*G).vexnum; ++j){
				if (((*G).arcs[i][j].adj == 1 && (*G).kind == DG)
					|| ((*G).arcs[i][j].adj != INFINITY && (*G).kind == DN)){
					if ((*G).arcs[i][j].info){
						free((*G).arcs[i][j].info);
						(*G).arcs[i][j].info = NULL;
					}
				}
			}
		}
	}
	else{ /* 无向 */
		for (i = 0; i < (*G).vexnum; ++i){ /* 释放边的相关信息(如果有的话) */
			for(j = i + 1; j < (*G).vexnum; ++j){
				if (((*G).arcs[i][j].adj == 1 && (*G).kind == AG) 
					|| ((*G).arcs[i][j].adj == INFINITY && (*G).kind == AN)){
					if((*G).arcs[i][j].info){ /* 有相关信息 */
						free((*G).arcs[i][j].info);
						(*G).arcs[i][j].info = NULL;
					}
				}
			}
		}
	}
	(*G).vexnum = 0;
	(*G).arcnum = 0;
	return;
}
VertexType* GetVex(MGraph G,int v)
{ /* 初始条件: 图G存在,v是G中某个顶点的序号。操作结果: 返回v的值 */
	if (v <0 || v >= G.vexnum){
		return ERROR;
	}
	return &G.vexs[v];
}
Status PutVex(MGraph *G,VertexType v,VertexType value)
{ /* 初始条件: 图G存在,v是G中某个顶点。操作结果: 对v赋新值value */
	int k;
	k = LocateVex(*G,v);
	if (k < 0){
		return ERROR;
	}
	strcpy((*G).vexs[k],value );
	return OK;
}
int FirstAdjVex(MGraph *G,VertexType v)
{	/* 初始条件: 图G存在,v是G中某个顶点 */
	/* 操作结果: 返回v的第一个邻接顶点的序号。若顶点在G中没有邻接顶点,则返回-1 */
	int i,j = 0,k;
	k = LocateVex(*G,v); /* k为顶点v在图G中的序号 */
	if ((*G).kind == AN || (*G).kind == DN ){/* 网 */
		j = INFINITY;
	}
	for(i = 0; i < (*G).vexnum; i++){
		if((*G).arcs[k][i].adj != j ){ 
			return i;
		}
	}
	return -1;
}
int NextAdjVex(MGraph *G,VertexType v,VertexType w)
{	/* 初始条件: 图G存在,v是G中某个顶点,w是v的邻接顶点 */
	/* 操作结果: 返回v的(相对于w的)下一个邻接顶点的序号, */
	/*           若w是v的最后一个邻接顶点,则返回-1 */
	int i,j=0,k1,k2;
	k1 = LocateVex(*G,v);
	k2 = LocateVex(*G,w);
	if ((*G).kind == AN || (*G).kind == DN){
		j = INFINITY;
	}
	for (i = k2+1; i < (*G).vexnum; i++){
		if ((*G).arcs[k1][i].adj != j){
			return j;
		}
	}
	return -1;
}

void InsertVex(MGraph *G,VertexType v)
{	/* 初始条件: 图G存在,v和图G中顶点有相同特征 */
	/* 操作结果: 在图G中增添新顶点v(不增添与顶点相关的弧,留待InsertArc()去做) */
	int i = 0;
	strcpy((*G).vexs[(*G).vexnum],v);
	for (i = 0; i < (*G).vexnum; i++){	
		if ((*G).kind%2 == 0){
			(*G).arcs[(*G).vexnum][i].adj = INFINITY;
			(*G).arcs[i][(*G).vexnum].adj = INFINITY;
		}
		else if((*G).kind % 2 != 0){
			(*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 += 1;/* 图G的顶点数加1 */
	return;
}

Status DeleteVex(MGraph *G,VertexType v)
{	/* 初始条件: 图G存在,v是G中某个顶点。操作结果: 删除G中顶点v及其相关的弧 */
	int i,j,k;
	VRType m = 0;
	
	k = LocateVex(*G,v);/* k为待删除顶点v的序号 */
	if (k < 0){
		return ERROR;
	}
	if ((*G).kind == AN || (*G).kind == DN){
		j = INFINITY;
	}
	for(j = 0; j < (*G).vexnum; j++){
		if((*G).arcs[j][k].adj != m){ /*有弧或有边*/
			if((*G).arcs[j][k].info != NULL){/* 有相关信息 */
				free((*G).arcs[j][k].info); /* 释放相关信息 */
			}
			(*G).arcnum--; /* 修改弧数 */
		}
		if ((*G).kind == DG || (*G).kind == DN){
			if((*G).arcs[k][j].info != NULL){/* 有相关信息 */
				free((*G).arcs[k][j].info); /* 释放相关信息 */
			}
			(*G).arcnum--; /* 修改弧数 */		
		}
	}
	for(j=k+1; j < (*G).vexnum; j++){/* 序号k后面的顶点向量依次前移 */
		strcpy((*G).vexs[j-1],(*G).vexs[j]);
	}
	for(i=0; i < (*G).vexnum; i++){
		for(j=k+1; j< (*G).vexnum; j++ ){
			(*G).arcs[i][j-1] = (*G).arcs[i][j];/* 移动待删除顶点之后的矩阵元素 */
		}
	}
	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;
}
Status InsertArc(MGraph *G,VertexType v,VertexType w)
{	/* 初始条件: 图G存在,v和W是G中两个顶点 */
	/* 操作结果: 在G中增添弧<v,w>,若G是无向的,则还增添对称弧<w,v> */
	int i,l,v1,w1;
	char *info,s[MAX_INFO];
	v1 = LocateVex(*G,v);/*头*/
	w1 = LocateVex(*G,w);/*尾*/
	if(v1 < 0 || w1 < 0){
		return ERROR;
	}
	(*G).arcnum++;/* 弧或边数加1 */
	if ((*G).kind == DN || (*G).kind == AN){
		printf("请输入此弧或边的权值:");
		scanf("%d",&(*G).arcs[v1][w1].adj);
	}
	else{/* 图 */
		(*G).arcs[v1][w1].adj = 1;
	}
	printf("是否有该弧或边的相关信息(0:无 1:有): ");
	scanf("%d%*c",&i);
	if(i){
		printf("请输入该弧或边的相关信息(<%d个字符):",MAX_INFO);
		gets(s);
		l = strlen(s);
		if(l){
			info = (char*)malloc((l+1)*sizeof(char));
			strcpy(info,s);
			(*G).arcs[v1][w1].info = info;
		}
	}
	if((*G).kind == AN || (*G).kind == AG){/*无向*/
		(*G).arcs[w1][v1].adj = (*G).arcs[v1][w1].adj;
		(*G).arcs[w1][v1].info = (*G).arcs[v1][w1].info;/* 指向同一个相关信息 */
	}
	return OK;
}

Status DeleteArc(MGraph *G,VertexType v,VertexType w)
{	/* 初始条件: 图G存在,v和w是G中两个顶点 */
	/* 操作结果: 在G中删除弧<v,w>,若G是无向的,则还删除对称弧<w,v> */
	int v1,w1;	
	v1 = LocateVex(*G,v);/*头*/
	w1 = LocateVex(*G,w);/*尾*/
	if(v1 < 0 || w1 < 0){
		return ERROR;
	}
	if((*G).kind == AG || (*G).kind == DG){
		(*G).arcs[v1][w1].adj = 0;	
	}
	else{
		(*G).arcs[v1][w1].adj = INFINITY;
	}
	if((*G).arcs[v1][w1].info != NULL){ /* 有其它信息 */
		free((*G).arcs[v1][w1].info);
		(*G).arcs[v1][w1].info = NULL;
	}
	/*无向图或表的情况下,删除对称弧*/
	if( (*G).kind == AG || (*G).kind == AN ){
		(*G).arcs[w1][v1].adj = (*G).arcs[v1][w1].adj;
		(*G).arcs[w1][v1].info = NULL;
	}
	(*G).arcnum--;
	return OK;
}
Boolean visited[MAX_VERTEX_NUM]; /* 访问标志数组(全局量) */
Status(*VisitFunc)(VertexType); /* 函数变量 */

void DFS(MGraph G,int v)
{ /* 从第v个顶点出发递归地深度优先遍历图G。算法7.5 */
	VertexType w1,v1;
	int w;

	visited[v] = TRUE;/* 设置访问标志为TRUE(已访问) */
	VisitFunc(G.vexs[v]); /* 访问第v个顶点 */
	strcpy(v1,*GetVex(G,v));
	for(w = FirstAdjVex(&G,v1); w > 0; w=NextAdjVex(&G,v1,strcpy(w1,*GetVex(G,w)))){
		if (!visited[w]){
			DFS(G,w); /* 对v的尚未访问的序号为w的邻接顶点递归调用DFS */
		}
	}
}

void DFSTraverse(MGraph G,Status(*Visit)(VertexType))
{	/* 初始条件: 图G存在,Visit是顶点的应用函数。算法7.4 */
	/* 操作结果: 从第1个顶点起,深度优先遍历图G,并对每个顶点调用函数Visit */
	/*           一次且仅一次。一旦Visit()失败,则操作失败 */
	int v;

	VisitFunc = Visit;/* 使用全局变量VisitFunc,使DFS不必设函数指针参数 */
	for (v=0; v < G.vexnum; v++){
		visited[v] = FALSE;/* 访问标志数组初始化(未被访问) */
	}
	for (v = 0; v < G.vexnum; v++){
		if(!visited[v]){
			DFS(G,v); /* 对尚未访问的顶点调用DFS */
		}
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
算法(Algorithm)是解题的步骤,可以把算法定义成解一确定类问题的任意一种特殊的方法。在计算机科学中,算法要用计算机算法语言描述,算法代表用计算机解一类问题的精确、有效的方法。算法+数据结构=程序,求解一个给定的可计算或可解的问题,不同的人可以编写出不同的程序,来解决同一个问题,这里存在两个问题:一是与计算方法密切相关的算法问题;二是程序设计的技术问题。算法和程序之间存在密切的关系。 算法是一组有穷的规则,它们规定了解决某一特定类型问题的一系列运算,是对解题方案的准确与完整的描述。制定一个算法,一般要经过设计、确认、分析、编码、测试、调试、计时等阶段。 对算法的学习包括五个方面的内容:① 设计算法。算法设计工作是不可能完全自动化的,应学习了解已经被实践证明是有用的一些基本的算法设计方法,这些基本的设计方法不仅适用于计算机科学,而且适用于电气工程、运筹学等领域;② 表示算法。描述算法的方法有多种形式,例如自然语言和算法语言,各自有适用的环境和特点;③确认算法。算法确认的目的是使人们确信这一算法能够正确无误地工作,即该算法具有可计算性。正确的算法用计算机算法语言描述,构成计算机程序,计算机程序在计算机上运行,得到算法运算的结果;④ 分析算法。算法分析是对一个算法需要多少计算时间和存储空间作定量的分析。分析算法可以预测这一算法适合在什么样的环境中有效地运行,对解决同一问题的不同算法的有效性作出比较;⑤ 验证算法。用计算机语言描述的算法是否可计算、有效合理,须对程序进行测试,测试程序的工作由调试和作时空分布组成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值