图——邻接矩阵的存储与基本操作

1.邻接矩阵

两个数组分别存储:数据元素(顶点)的信息和数据元素之间的关系(边或弧)的信息。
其形式描述如下:

// ----- 图的数组(邻接矩阵)存储表示 ----- //
// 常量定义
# define INFINITY -1           // 最大权重,表示不邻接关系 
# define MAX_VERTEX_NUM 10     // 最大顶点个数 
typedef int VRType;            // 顶点间的关系类型,如0/1表示邻接否
typedef char InfoType;         // 弧段的信息类型
typedef char VertexType;       // 顶点信息类型
typedef enum { DG, DN, UDG, UDN } GraphKind;   // 有向图、有向网、 无向图、无向网 

// 图的邻接矩阵存储结构定义
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;

2.图的创建

无向网(带权值的无向图)

//构造无向网UDN
void createUDN(MGraph& G) {
	int incInfo; //表示有无弧上信息,0表示弧不含任何信息
	cin >> G.vexnum >> G.arcnum >> incInfo;
	int i, j;
	for (i = 0; i < G.vexnum; i++) cin >> G.vexs[i];   // 输入顶点的信息
	for (i = 0; i < G.vexnum; i++) {
		for (j = 0; j < G.vexnum; j++) G.arcs[i][j] = { INFINITY,NULL }; // 初始化邻接矩阵
	}
	for (i = 0; i < G.arcnum; i++) {  // 输入弧v1-v2的信息
		VertexType v1, v2;
		int w;
		cin >> v1 >> v2 >> w;
		int l1 = LocateVex(G, v1);
		int l2 = LocateVex(G, v2);
		G.arcs[l1][l2].adj = w;   // 输入弧上权重信息
		if (incInfo) cin >> *G.arcs[l1][l2].info;  // 若有弧上信息,则输入
		G.arcs[l2][l1] = G.arcs[l1][l2];
	}
}

无向图(不带权值的无向图)

//构建无向图UDG
void createUDG(MGraph& G) {
	int IncInfo; //IncInfo为0表示各个边不存储信息
	cin >> G.vexnum >> G.arcnum >> IncInfo;
	int i, j;
	for (i = 0; i < G.vexnum; i++) cin >> G.vexs[i]; //输入各个顶点的数据
	for (i = 0; i < G.vexnum; i++) {
		for (j = 0; j < G.vexnum; j++) G.arcs[i][j] = { -1,NULL }; 
		//初始化邻接矩阵,一般是初始化为0,
		//但是这里我与前面UDN一致是为了通用一些查找函数
	}
	// 输入邻接的点来构造邻接边
	for (i = 0; i < G.arcnum; i++) {
		VertexType v1, v2;
		cin >> v1 >> v2;
		int index1 = LocateVex(G, v1);
		int index2 = LocateVex(G, v2);
		G.arcs[index1][index2].adj = 1; // 1表示邻接
		if (IncInfo) cin >> *G.arcs[index1][index2].info;
		G.arcs[index2][index1] = G.arcs[index1][index2];  // 无向图对称构造
	}
}

3.图的基本操作

3.1 LocateVex

int LocateVex(MGraph G, VertexType u); // 返回顶点u在图中的位置

int LocateVex(MGraph G, VertexType u) {
	int i;
	for (i = 0; i < G.vexnum && G.vexs[i] != u; i++);
	return i;
}
3.2 FirstAdjVex

int FirstAdjVex(MGraph G, VertexType u); // 返回图G中u的第一个邻接节点

// 返回图G中u的第一个邻接节点
int FirstAdjVex(MGraph G, VertexType u) {
	int index = LocateVex(G, u);
	int i;
	for (i = 0; i < G.vexnum; i++) {
		if (G.arcs[index][i].adj != -1) break;
	}
	if (i == G.vexnum) return -1;  // 没有临界点
	else return i;
} 
3.3 NextAdjVex

int NextAdjVex(MGraph G, VertexType v, VertexType u); // 返回G中顶点v相对于u的下一邻接点

int NextAdjVex(MGraph G, VertexType v, VertexType u) {
	int indexV = LocateVex(G, v);
	int indexU = LocateVex(G, u);
	int index;
	for (index = indexU + 1; index < G.vexnum && G.arcs[indexV][index].adj == -1; index++);
	if (index == G.vexnum) return -1;
	else return index;
}
3.4 showMatrix

void showMatrix(MGraph G); // 打印图的邻接矩阵

void showMatrix(MGraph G) {
	for (int i = 0; i < G.vexnum; i++) {
		for (int j = 0; j < G.vexnum; j++) cout << G.arcs[i][j].adj << " ";
		cout << endl;
	}
}

4.图的遍历

图的遍历(Traversing Graph) 和树的遍历类似,即从图中某一顶点出发遍历图中其余顶点,且使每一个顶点被访问一次。
为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已被访问过的顶点。为此,我们可以设一个辅助数组Visited[N]来标记某一顶点是否被访问过了,初始化为False

4.1 深度优先遍历

类似于树的先根遍历,是树的先根遍历的推广。

// 图的深度优先遍历
void DFSTraverse(MGraph G) {
	int* visited = new int[G.vexnum]; // 访问标志数组
	for (int i = 0; i < G.vexnum; i++) visited[i] = 0; // 初始化未被访问
	for (int i = 0; i < G.vexnum; i++) {
		if (!visited[i]) DFS(G, i, visited); // 对尚未访问的顶点i调用DFS,
											 // 因为图G可能不是连通图
	}
}
// 从图中第v个顶点开始进行深度优先遍历
void DFS(MGraph G, int v, int* visited) {
	visited[v] = 1; //先访问顶点v
	cout << G.vexs[v] << " ";
	for (int w = FirstAdjVex(G, G.vexs[v]); w >= 0; w = NextAdjVex(G, G.vexs[v], G.vexs[w])) {
		if (!visited[w]) DFS(G, w, visited); // 对v的尚未访问的邻接顶点w递归调用DFS
	}
}
4.2 广度优先遍历

广度优先搜索(Broadth First Search) 类似于树层次遍历的过程,即以v为初始点,由近即远,一次访问和v有路径相通且路径长度为1,2,…的顶点。此过程可借助于队列实现。

void BFSTraverse(MGraph G) {
// 按照广度优先非递归遍历图G,使用辅助队列Q和访问标志数组visited。
	int* visited = new int[G.vexnum];
	queue<int> Q;
	int i;
	for (i = 0; i < G.vexnum; i++) visited[i] = 0;
	for (i = 0; i < G.vexnum; i++) {
		if (!visited[i]) { //访问顶点i
			visited[i] = 1;
			cout << G.vexs[i] << " ";
			Q.push(i);
			while (!Q.empty()) {
				int u = Q.front();
				Q.pop(); //取出对头顶点
				for (int w = FirstAdjVex(G, G.vexs[u]); w >= 0; w = NextAdjVex(G, G.vexs[u], G.vexs[w])) {
					if (!visited[w]) {  // w为u的尚未访问的邻接顶点
						visited[w] = 1;
						cout << G.vexs[w] << " ";
						Q.push(w);
					}
				}
			}
		}
	}
}

5.测试代码

#include <iostream>
#include <string>
#include <queue>

using namespace std;
// 常量定义
# define INFINITY -1           // 最大权重,表示不邻接关系 
# define MAX_VERTEX_NUM 10     // 最大顶点个数 
typedef int VRType;            // 顶点的关系类型
typedef char InfoType;         // 弧段的信息类型
typedef string VertexType;       // 顶点信息类型
typedef enum { DG, DN, UDG, UDN } GraphKind;   // 有向图、有向网、 无向图、无向网 
// ---- 图的邻接矩阵表示 ---- //
// 图的邻接矩阵存储结构定义
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);  // 返回顶点u在图中的位置
int FirstAdjVex(MGraph G, VertexType u); // 返回图G中u的第一个邻接节点
int NextAdjVex(MGraph G, VertexType v, VertexType u); // 返回G中顶点v相对于u的下一邻接点
//构造无向网UDN
void createUDN(MGraph& G);   //创建无向网
void createUDG(MGraph& G);   //创建无向网
// 图的深度优先遍历
void DFSTraverse(MGraph G);
// 从图中第v个顶点开始,递归进行深度优先遍历图G
void DFS(MGraph G, int v, int* visited);
// 图的广度优先遍历
void BFSTraverse(MGraph G);
// 打印图的邻接矩阵
void showMatrix(MGraph G);
int main() {
	
	MGraph G;
	G.kind = UDN;
	createUDN(G);
	printf("无向网的邻接矩阵:\n");
	showMatrix(G);
	printf("顶点F的第一个邻接点:");
	int index = FirstAdjVex(G, "F");
	cout << G.vexs[index] << endl;
	printf("顶点F相对于A的下一邻接点:");
	index = NextAdjVex(G, "F", "A");
	cout << G.vexs[index] << endl;
	
	MGraph G2; // 无向图
	G2.kind = UDG;
	createUDG(G2);
	printf("无向图的邻接矩阵:\n");
	showMatrix(G2);
	printf("无向图的深度优先遍历:");
	DFSTraverse(G2);
	cout << endl;
	printf("无向图的广度优先遍历:");
	BFSTraverse(G2);
	cout << endl;
	system("pause");
}

测试用例:

无向网
6 10 0
A B C D E F
A B 5
A C 8
A D 7
A F 3
B C 4
C D 5
C F 9
D E 5
D F 6
E F 1

无向图
8 9 0
V1 V2 V3 V4 V5 V6 V7 V8
V1 V2
V1 V3
V2 V4
V2 V5
V3 V6
V3 V7
V4 V8
V5 V8
V6 V7

即下面两张图:
无向网实例
无向图实例

参考资料

《数据结构 C语言版》 严蔚敏著

猜你喜欢

转载自blog.csdn.net/Africa_South/article/details/88553068