用两个数组分别存储:数据元素(顶点
)的信息和数据元素之间的关系(边或弧
)的信息。
其形式描述如下:
// ----- 图的数组(邻接矩阵)存储表示 ----- //
// 常量定义
# 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;
无向网(带权值的无向图)
//构造无向网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]; // 无向图对称构造
}
}
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;
}
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;
}
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;
}
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;
}
}
图的遍历(Traversing Graph) 和树的遍历类似,即从图中某一顶点出发遍历图中其余顶点,且使每一个顶点仅
被访问一次。
为了避免同一顶点被访问多次,在遍历图的过程中,必须记下每个已被访问过的顶点。为此,我们可以设一个辅助数组Visited[N]
来标记某一顶点是否被访问过了,初始化为False
。
类似于树的先根遍历,是树的先根遍历的推广。
// 图的深度优先遍历
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
}
}
广度优先搜索(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);
}
}
}
}
}
}
#include
#include
#include
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语言版》 严蔚敏著