7.2 图的基础综合运算(邻接表存储)

实验7.2 图的基础综合运算(邻接表存储)

实验目的

  • 理解邻接表存储方式方法
  • 理解连通图以邻接表存储时的深度-广度优先搜索遍历算法及实现

说明

  • 参考代码里面固定创建队列和顶点数组容量为100, 邻接矩阵固定 100*100, 有点浪费内存,改成动态创建了
  • 代码里面很多变量名只有单个字符,读起来不方便,都改成有意义的名字了
  • tu2.txt 需要放到与编译后可执行文件相同的路径下(不是源代码目录),对于使用 VisualStudio 的同学,可执行文件一般在你项目路径下的 Debug 文件夹内

代码

tu2.txt

该文件格式为:
结点数 边数
结点名字(char)
边1(结点1 结点2)
边2
...

4 4
A B C D
A B
A C
A D
B D

Graph_AdjList.cpp

#include <iostream>
#include <fstream>
using namespace std;

#define OK 1
#define ERROR 0
#define OVERFLOW -2

#define GRAPH_FILE "./tu2.txt" // 输入的文件路径

typedef char VerTexType; //假设顶点的数据类型为字符型
typedef int ArcType;	 //假设边的权值类型为整型

struct ArcNode
{					  //边结点
	int adjvex;		  //该边所指向的顶点的位置
	ArcNode *nextarc; //指向下一条边的指针
};

struct VNode
{
	VerTexType data;   //顶点信息
	ArcNode *firstarc; //指向第一条依附该顶点的边的指针
};

struct ALGraph
{
	VNode *vertices;	//邻接表
	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 在头结点列表的下标
int LocateVex(ALGraph &G, VerTexType v)
{
	for (int i = 0; i < G.vexnum; ++i)
		if (G.vertices[i].data == v)
			return i;
	return -1;
}

// 采用邻接表表示法,创建无向图G
void CreateUDG(ALGraph &G)
{
	fstream file;
	file.open(GRAPH_FILE);
	if (!file)
	{
		cout << "没有找到图文件!" << endl;
		exit(ERROR);
	}

	file >> G.vexnum >> G.arcnum;	  // 输入总顶点数,总边数
	G.vertices = new VNode[G.vexnum]; // 创建表头结点列表

	for (int i = 0; i < G.vexnum; ++i)
	{
		file >> G.vertices[i].data;		  // 输入顶点值
		G.vertices[i].firstarc = nullptr; // 初始化表头结点的指针域为空
	}

	for (int k = 0; k < G.arcnum; ++k)
	{
		VerTexType v1, v2;
		file >> v1 >> v2;		  // 输入一条边依附的两个顶点值
		int i = LocateVex(G, v1); // 找到这两个顶点的下标
		int j = LocateVex(G, v2);

		ArcNode *p1 = new ArcNode; // 创建一个边结点, 插到 i 号下标结点的边表上
		p1->adjvex = j;
		p1->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p1;

		// 相对应的,j结点的边链表也要插入一个边结点,有向图不用下面这个
		ArcNode *p2 = new ArcNode;
		p2->adjvex = i;
		p2->nextarc = G.vertices[j].firstarc;
		G.vertices[j].firstarc = p2;
	}
}

// 深度优先递归遍历连通图G
void DFS(ALGraph &G, int vex_index, bool *visited)
{
	cout << G.vertices[vex_index].data << '\t' << flush; // 访问下标为 vex_index 的结点
	visited[vex_index] = true;
	ArcNode *p = G.vertices[vex_index].firstarc; // p 指向下标为 vex_index 结点的边链表的第一个边结点
	while (p)
	{
		int adj_vex_index = p->adjvex;		// 与它相邻的第一个结点的下标
		if (!visited[adj_vex_index])		// 如果没有访问过
			DFS(G, adj_vex_index, visited); // 继续递归遍历它
		p = p->nextarc;						// 继续遍历下一条边
	}
}

// 广度优先遍历
void BFS(ALGraph &G, int vex_index)
{
	bool *visited = new bool[G.vexnum]{}; // 创建 visited 数组记录已经访问过的结点下标

	cout << G.vertices[vex_index].data << '\t'; // 访问下标为 vex_index 的结点
	visited[vex_index] = true;

	SqQueue Q;
	int visited_index;
	InitQueue(Q, G.vexnum); // 创建一个长度为结点数的队列
	EnQueue(Q, vex_index);	// 访问过的下标入队
	while (!QueueEmpty(Q))
	{
		DeQueue(Q, visited_index);						 // 取出第一个访问过的结点
		ArcNode *p = G.vertices[visited_index].firstarc; // 找到它的第一个边结点
		while (p)
		{
			if (!visited[p->adjvex]) // 如果它存在且为被访问
			{
				cout << G.vertices[p->adjvex].data << '\t'; // 访问它
				visited[p->adjvex] = true;
				EnQueue(Q, p->adjvex); // 访问过的下标入队
			}
			p = p->nextarc; // 沿着邻接点链表一直往下遍历
		}
	}
	delete[] visited;
}

// 显示图
void Display(ALGraph &G)
{
	for (int i = 0; i < G.vexnum; ++i)
	{
		VNode temp = G.vertices[i];
		ArcNode *p = temp.firstarc;
		if (!p)
			cout << G.vertices[i].data << "|" << endl;
		else
		{
			cout << temp.data << "|";
			while (p)
			{
				cout << " -> ";
				cout << G.vertices[p->adjvex].data;
				p = p->nextarc;
			}
		}
		cout << endl;
	}
}

// 销毁图
void DestroyGraph(ALGraph &G)
{
	for (int i = 0; i < G.vexnum; i++)
	{
		ArcNode *p = G.vertices[i].firstarc;
		while (p)
		{
			ArcNode *t = p;
			delete t;
			p = p->nextarc;
		}
	}
	delete[] G.vertices;
}

int main()
{
	cout << "************算法6.6 采用邻接表表示图的深度优先搜索遍历**************" << endl;
	ALGraph G;
	CreateUDG(G);
	cout << "无向连通图G创建完成!" << endl;
	cout << "**************邻接表表示无向连通图*******************" << endl;
	Display(G);
	cout << "********************************" << endl;
	VerTexType vex_name;
	int i;
	do
	{
		cout << "请输入遍历连通图的起始点: ";
		cin >> vex_name;
		for (i = 0; i < G.vexnum; ++i)
		{
			if (vex_name == G.vertices[i].data)
				break;
			else
				cout << "该点不存在,请重新输入!" << endl;
		}
	} while (i >= G.vexnum);

	cout << "深度优先搜索遍历连通图结果:" << endl;
	bool *visited = new bool[G.vexnum]{}; // 动态创建visited数组保存结点访问状态
	DFS(G, i, visited);
	delete[] visited;
	cout << "\n广度优先搜索遍历连通图结果:" << endl;
	BFS(G, i);
	cout << endl;
	DestroyGraph(G);
}

运行截图

OOP 版

还没写...

posted @ 2020-10-30 20:13  zaxtyson  阅读(317)  评论(0编辑  收藏  举报