7.1 图的基础综合运算(邻接矩阵存储)
实验7- 图的基础综合运算(邻接矩阵存储)
实验目的
- 理解邻接矩阵存储方式方法
- 理解连通图以邻接矩阵存储时的深度-广度优先搜索遍历算法及实现
说明
- 虽然实验是做连通图,但是看参考代码是想创建连通网,所以下面的代码其实是创建了一个网,输入的时候需要给权值
- 参考代码里面固定创建队列和顶点数组容量为100, 邻接矩阵固定 100*100, 有点浪费内存,改成动态创建了
- 用于寻找第一个邻接结点和下一个邻接结点的函数代码几乎重复, 统一为
FindAdjVex()
了 - 代码里面很多变量名只有单个字符,读起来不方便,都改成有意义的名字了
tu.txt
需要放到与编译后可执行文件相同的路径下(不是源代码目录),对于使用 VisualStudio 的同学,可执行文件一般在你项目路径下的Debug
文件夹内- 删删改改基本上删的差不多了(滑稽),重写了注释,自己看吧
代码
tu.txt
该文件格式为:
结点数 边数
结点名字(char)
边1(结点1 结点2 权值)
边2
...
如
4 4
A B C D
A B 3
A C 4
A D 5
B D 6
Graph_AdjMatrix.cpp
#include <iostream>
#include <fstream>
#include <limits> // INT32_MAX
using namespace std;
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define GRAPH_FILE "./tu.txt" // 输入的文件路径
typedef char VerTexType; //假设顶点的数据类型为字符型
typedef int ArcType; //假设边的权值类型为整型
struct Graph
{
VerTexType *vexs; //顶点表
ArcType **arcs; //邻接矩阵
int vexnum, arcnum; //图的当前点数和边数
};
struct SqQueue
{
size_t capacity; // 最大大小
ArcType *base; //初始化的动态分配存储空间
int front; //头指针,若队列不空,指向队头元素
int rear; //尾指针,若队列不空,指向队尾元素的下一个位置
};
// 初始化循环队列
int InitQueue(SqQueue &Q, size_t size)
{
Q.capacity = size;
Q.base = new ArcType[size];
if (!Q.base)
return OVERFLOW;
Q.front = Q.rear = 0;
return OK;
}
// 销毁循环队列
void DestroyQueue(SqQueue &Q)
{
delete[] Q.base;
}
// 判空
bool QueueEmpty(SqQueue &Q)
{
return Q.front == Q.rear;
}
// 判满
bool QueueFull(SqQueue &Q)
{
return (Q.rear + 1) % Q.capacity == Q.front;
}
// 入队 e
int EnQueue(SqQueue &Q, ArcType e)
{
if (QueueFull(Q))
return ERROR;
Q.base[Q.rear] = e;
Q.rear = (Q.rear + 1) % Q.capacity;
return OK;
}
// 出队
int DeQueue(SqQueue &Q, ArcType &u)
{
if (QueueEmpty(Q))
return ERROR;
u = Q.base[Q.front];
Q.front = (Q.front + 1) % Q.capacity;
return OK;
}
// 确定结点 v 在 G 中的下标
int LocateVex(Graph &G, VerTexType v)
{
for (int i = 0; i < G.vexnum; ++i)
if (G.vexs[i] == v)
return i;
return -1;
}
// 采用邻接矩阵表示法,创建无向网G
void CreateUDN(Graph &G)
{
fstream file;
file.open(GRAPH_FILE);
if (!file)
{
cout << "没有找到图文件!" << endl;
exit(ERROR);
}
file >> G.vexnum >> G.arcnum; //输入总顶点数,总边数
G.vexs = new VerTexType[G.vexnum]; // 创建顶点数组
G.arcs = new ArcType *[G.vexnum]; // 创建一个矩阵
for (int i = 0; i < G.vexnum; ++i)
G.arcs[i] = new ArcType[G.vexnum];
for (int i = 0; i < G.vexnum; ++i)
file >> G.vexs[i]; // 输入顶点值
for (int i = 0; i < G.vexnum; ++i) // 初始化矩阵的值为INT的最大值,表示两个结点没有边
for (int j = 0; j < G.vexnum; ++j)
G.arcs[i][j] = INT32_MAX;
//构造邻接矩阵
for (int k = 0; k < G.arcnum; ++k)
{
VerTexType v1, v2;
file >> v1 >> v2; // 输入一条边依附的两个顶点值
int i = LocateVex(G, v1);
int j = LocateVex(G, v2); //确定v1和v2在G中的位置,即顶点数组的下标
ArcType w;
file >> w; // 输入边的权值
G.arcs[i][j] = w; //边<v1, v2>的权值置为w
G.arcs[j][i] = w; //置<v1, v2>的对称边<v2, v1>的权值为w
}
file.close();
}
// 返回下标为 vex_index 结点的相邻结点下标,从 offset 往后搜索
int FindAdjVex(Graph &G, int vex_index, int offset = 0)
{
for (int i = offset; i < G.vexnum; ++i)
{
if (G.arcs[vex_index][i] != INT32_MAX)
return i;
}
return -1;
}
// 按广度优先非递归遍历连通图G
void BFS(Graph &G, int vex_index)
{
bool *visited = new bool[G.vexnum]{}; // 创建大小为结点数的 visited 数组并初始化为 false, 记录结点是否访问过
cout << G.vexs[vex_index] << '\t'; // 输出第一个结点
visited[vex_index] = true; // 标记该结点已经访问过
SqQueue Q;
InitQueue(Q, G.vexnum);
EnQueue(Q, vex_index); // 已经访问过的结点入队
int visited_index; // 已经访问过的结点数组下标
int adj_vex_index; // 邻接结点的数组下标
while (!QueueEmpty(Q))
{
DeQueue(Q, visited_index); // 队头元素出队, 存到 visited_index
adj_vex_index = FindAdjVex(G, vex_index); // 找 vex_index 号结点的第一个邻接结点下标
while (adj_vex_index >= 0) // 如果找到了
{
if (!visited[adj_vex_index]) // 且该结点未被访问
{
cout << G.vexs[adj_vex_index] << '\t'; // 输出结点值
visited[adj_vex_index] = true; // 标记该节点已经访问过
EnQueue(Q, adj_vex_index); // 访问过之后入队
}
adj_vex_index = FindAdjVex(G, vex_index, adj_vex_index + 1); // 找到下一个邻接结点的下标
}
}
delete[] visited; // 记得删掉 visited 数组
DestroyQueue(Q); // 用完删掉, 防止内存泄漏
}
// 按深度优先递归遍历连通图G
void DFS(Graph &G, int vex_index, bool *visited)
{
cout << G.vexs[vex_index] << '\t';
visited[vex_index] = true; //访问第v个顶点,并置访问标志数组相应分量值为true
for (int i = 0; i < G.vexnum; i++)
if ((G.arcs[vex_index][i] != INT32_MAX) && (!visited[i]))
DFS(G, i, visited);
}
// 显示图的邻接矩阵
void Display(Graph G)
{
for (int i = 0; i < G.vexnum; i++)
{
for (int j = 0; j < G.vexnum; j++)
if (G.arcs[i][j] != INT32_MAX)
cout << G.arcs[i][j] << '\t';
else
cout << "∞" << '\t';
cout << endl;
}
}
// 销毁图中动态申请的空间
void DestroyGraph(Graph &G)
{
delete[] G.vexs;
for (int i = 0; i < G.vexnum; i++)
delete[] G.arcs[i];
}
int main()
{
cout << "************算法6.7 广度优先搜索遍历连通图**************\n\n";
Graph G;
CreateUDN(G);
cout << "无向连通图G创建完成!\n\n";
cout << "**************************\n\n";
Display(G);
cout << "**************************\n\n";
VerTexType vex_name;
int i;
do
{
cout << "请输入遍历连通图的起始点: ";
cin >> vex_name;
for (i = 0; i < G.vexnum; ++i)
{
if (vex_name == G.vexs[i])
break;
else
cout << "该点不存在,请重新输入!" << endl;
}
} while (i >= G.vexnum);
cout << "深度优先搜索遍历连通图结果:" << endl;
bool *visited = new bool[G.vexnum]{};
DFS(G, i, visited);
delete[] visited; // 用完删掉 visited
cout << endl;
cout << "广度优先搜索遍历连通图结果:" << endl;
BFS(G, i);
cout << endl;
DestroyGraph(G);
}
运行截图
OOP 版
还没写...