图的结构与操作

图的结构

1、邻接矩阵结构

#define MAXSIZE 100
#define INFINITY 65535  //无穷大,表示两点直接没有路径连接
typedef struct Mgraph{
	char vex[MAXSIZE];  //定义顶点数组
	int arc[MAXSIZE][MAXSIZE];  //定义邻接矩阵
	int numvex, numedge;    //定义顶点个数和边的个数
};
无向矩阵的创建

void CreatMgraph(Mgraph *G){
	int i, j, k, weight; //weight表示边上的权值
	scanf("%d,%d", &G->numvex, &G->numedge); //读入顶点个数和边个数
	for (i = 0; i < numvex; i++)
		scanf("%c", &G->vex[i]);   //读入顶点信息到数组中
	for (i = 0; i < numvex; i++)
		for (j = 0; j < numvex; j++)
			G->arc[i][j] = INFINITY;  //初始化邻接矩阵数组为无穷大
	for (k = 0; k < numedge; k++){
		scanf("%d,%d,%d", &i, &j, &weight);  //读入边之间的路径(vi,vj)和权值
		G->arc[i][j] = weight;
		G->arc[j][i] = weight; //此处为无向图,因此矩阵的权值是对角对称结构。如果为有向图,则需要另考虑权值
	}
}

2、邻接表的结构

typedef struct EdgeNode{
	int adjvex;  //邻接点对应的下标
	int weight;  //权值
	struct EdgeNode *next;
};
typedef struct VexNode{  //顶点结点
	int data;    //顶点信息
	struct EdgeNode *first;  //头指针
};
typedef struct Graphlist{  //邻接表结构
	VexNode adjlist[MAXSIZE];
	int numvex, numedge;  //顶点数和边数
};
无向图邻接表的创建

void CreatGraphlist(Graphlist *G){
	int i, j, k,w;
	scanf("%d,%d", &G->numvex, &G->numedge); //读入边和顶点的个数
	for (i = 0; i < numvex; i++){
		scanf("%c", &G->adjlist[i].data);  //读入顶点信息
		G->adjlist[i].first = nullptr;  //初始化头指针
	}
	for (k = 0; k < numedge; k++){
		scanf("%d,%d,%d", &i, &j, &w); //读入边(vi,vj)以及对应的权值
		EdgeNode *e = (EdgeNode *)malloc(sizeof(EdgeNode));
		e->adjvex = j;
		e->weight = w;
		e->next = G->adjlist[i].first;
		G->adjlist[i].first = e;   //此处用的是链表操作的头插法
	}			
}
还有十字链表、邻接多重链表、边集数组结构


关于图的遍历,主要为两种,深度优先遍历DFS和广度优先遍历BFS

广度优先遍历DFS--类似于树的前序遍历操作,

邻接矩阵的深度优先遍历

bool visited[MAX]; //使用一个标志数组来存储访问的信息
void DFS(MGraph G, int i){   //从第i个顶点开始遍历的DFS递归算法
	int j;
	visited[i] = true;     //true表示该顶点被访问过
	printf("%c", G.vex[i]);
	for (j = 0; j <G.numvex; j++)
		if (G.arc[i][j] != INFINITY &&!visited[j])
			DFS(G, j);	
}
void DFSTraverse(MGraph G){  //DFS遍历操作
	int i;
	for (i = 0; i <G.numvex; i++)
		visited[i] = false; //初始化标志数组,表示所有顶点都未访问过
	for (i = 0; i < G.numvex; i++)
		if (!visited[i])   // 连通图的话,只执行一次,非连通图会至少两次
			DFS(G, i);
}

邻接表的深度优先遍历

void DFS(Graphlist *G, int i){  //从第i个顶点开始遍历的DFS递归算法
	EdgeNode *p;  //相当于邻接矩阵中的j
	visited[i] = true;
	printf("%c", G->adjlist[i].data);
	p = G->adjlist[i].first; //相当于j=0
	while (p){
		if (!visited[i])
			DFS(G, p->adjvex);
		p = p->next;    //相当于j++
	}
}
void DFSTraverse(Graphlist *G){   //DFS遍历操作
	int i;
	for (i = 0; i < G->numvex; i++)
		visited[i] = false;
	for (i = 0; i < G->numvex; i++)
		if (!visited[i]) // 连通图的话,只执行一次,非连通图会至少两次
			DFS(G, i);
}


广度优先遍历BFS --类似于树的层遍历方式,需要借助一个队列辅助来实现

队列Queue的操作在前面总结当中提过,不再重新定义,不过要注意,根据实际情况,队列当中的数据类型可能会是图 sturct MGraph类型或者struct Graphlist类型

邻接矩阵的广度遍历BFS

void BFSTraverse(MGraph G){
	int i, j;
	Queue Q;  //辅助队列
	for (i = 0; i < G.numvex; i++)
		visited[i] = false;
	InitQueue(&Q); //初始化队列
	for (i = 0; i < G.numvex; i++){
		if (!visited[i]){
			visited[i] = true;    // 连通图的话,只执行一次,非连通图会至少两次
			printf("%c", G.vex[i]);
			EnQueue(&Q, &i);    //从第i个顶点开始BFS遍历
			while (!QueueEmpty(Q)){  //队列不为空的时候继续遍历
				Dequeue(&Q, &i);
					for (j = 0; j < G.numvex; j++){
					if (G.arc[i][j] != INFINITY && !visited){  //判断该顶点与其他顶点是否相连,并打印相连的顶点
						visited[j] = true;
						printf("%c", G.vex[j]);
						Enqueue(&Q, &i);          //将相邻的顶点存入队列中
					}
				}
			}
		}
	 }
}

邻接表的BFS

void BFSTraverse(Graphlist *G){
	EdgeNode *p;  //相当于邻接表中的j
	int i;
	Queue Q;
	for (i = 0; i < G->numvex; i++)
		visited[i] = false;
	InitQueue(&Q);
	for (i = 0; i < G->numvex; i++){
		if (!visited[i]){
			visited[i] = true;  // 连通图的话,只执行一次,非连通图会至少两次
			printf("%c", G->adjlist[i].data);
			EnQueue(&Q, &i);
			while (!QueueEmpty(Q)){  //队列不为空的时候继续遍历
				DeQueue(&Q, &i);
				p = G->adjlist[i].first; //相当于j=0
				while (p){
					if (!visited[p->adjvex]){
						visited[p->adjvex] = true;
						printf("%c", G->adjlist[p->adjvex].data);
						Enqueue(&Q, &i);
					}
					p = p->next;  //相当于j++
				}
			}

		}
	}
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值