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 版

还没写...

posted @ 2020-10-30 14:04  zaxtyson  阅读(404)  评论(0编辑  收藏  举报