数据结构与算法分析实验8 实现图的储存和遍历(C++版本)

1.上机名称

实现图的存储和遍历

2.上机要求

对无向图、无向网、有向图、有向网进行如下操作:
(1)建立图/网的邻接矩阵,并进行输出。
(2)建立图/网的邻接表,并进行输出。
(3)对图/网进行深度优先遍历。
(4)对图/网进行广度优先遍历。
(5)销毁图/网。

3.上机环境

visual studio 2022
Windows11 家庭版 64位操作系统

4.程序清单(写明运行结果及结果分析)

4.1 代码部分

4.1.1 头文件Graph.h中,内容如下:

按照实验要求,本次实验构建的是无向图,为后续实验方便,引进图的种类标志,尝试将有向版本、网的版本统一起来。

#pragma once
#include<iostream>
#include<queue>
enum GraphKind{//枚举类型,无向图,无向网,有向图,有向网
	UN_GRAPH,UN_NET,_GRAPH,_NET
};

typedef char Data;	//图的顶点里的数据元素
typedef int flag;	//用于标注是否被遍历

typedef struct NeighbourNode {//邻接表的一个成员
	int id;			//邻接表里的下标
	int weight;		//权重,默认为0
}NBnode, * pNBnode;

typedef struct GNode {//定义图的顶点元素结构
	Data data;				//数据成员
	pNBnode* LNeighbours;	//左邻(入)接表
	pNBnode* RNeighbours;   //右邻(出)接表
	flag is_visited;		//标注是否被遍历
	int LN_cnt;				//左邻数
	int RN_cnt;				//右邻数
}Gnode, * pGnode;

图类的定义

class Graph {
public:
	//创建一个顶点数为vex_num的图,图的类型为Kind
	Graph(int vex_num = 0, int Kind = 0);
	//通过邻接矩阵创建图	
	Graph(int** array_for_summon, int size, int Kind);
	~Graph();								//析构函数
	void insert(Data data);					//插入顶点
	void setdata(int id, Data data);		//设置顶点数据
	void link(int id1,int id2,int weight = 1);	    //使两个顶点之间产生边关联,weight == 0 表示无关联。
	void CreateArray();						//通过传入的邻接矩阵生成图,并对矩阵的合法性进行检测
	void makeneighbour(int id1, int id2, int weight = 1);//造邻居
	void printNBS(int id);	//打印id顶点的邻接表
	void printARR();		//打印图的邻接矩阵
	void DFS(int firstid = 0);			//从下标为firstid的顶点深度优先遍历
	void BFS(int firstid = 0);			//从下标为firstid的顶点广度优先遍历
protected:
	void SetFlag();						//为遍历初始化flag
	void DFSearch(int firstid = 0);		
	void BFSearch(int firstid = 0);
	int vex_num;		//顶点数量
	int** GArray;		//邻接矩阵
	int GKind;			//图的类型标记
	pGnode* vex;		//存放顶点的顺序表
};

4.1.2 实现文件Graph.cpp中,内容如下:

#include "Graph.h"

通过节点数初始化

Graph::Graph(int vex_num , int Kind ){
	this->vex_num = vex_num;
	vex = new pGnode[vex_num];
	for (int i = 0; i < vex_num; i++) {
		vex[i] = new Gnode;
	}
	for (int i = 0; i < vex_num; i++) {
		vex[i]->LN_cnt = 0;
		vex[i]->RN_cnt = 0;
		vex[i]->LNeighbours = new pNBnode;
		vex[i]->RNeighbours = new pNBnode;
		vex[i]->is_visited = 0;
	}
	GArray = new int* [vex_num]; for (int i = 0; i < vex_num; i++)GArray[i] = new int[vex_num];
	for (int i = 0; i < vex_num; i++)for (int j = 0; j < vex_num; j++) GArray[i][j] = 0;
	GKind = Kind;
}

通过array初始化

Graph::Graph(int** array_for_summon, int size, int Kind){   
	vex_num = size;
	vex = new pGnode[vex_num];
	for (int i = 0; i < vex_num; i++) {
		vex[i] = new Gnode;
	}
	for (int i = 0; i < vex_num; i++) {
		vex[i]->LN_cnt = 0;
		vex[i]->RN_cnt = 0;
		vex[i]->LNeighbours = new pNBnode;
		vex[i]->RNeighbours = new pNBnode;
		vex[i]->is_visited = 0;
	}
	GKind = Kind;
	GArray = new int*[size];for (int i = 0; i < size; i++) GArray[i] = new int[size];
	for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) GArray[i][j] = array_for_summon[i][j];
	CreateArray();
}

析构函数

Graph::~Graph(){
	for (int i = 0; i < vex_num; i++) delete[]GArray[i];
	delete[]GArray;
	for (int i = 0; i < vex_num; i++) {
		delete[]vex[i]->LNeighbours;
		delete[]vex[i]->RNeighbours;
		vex[i]->LN_cnt = 0;
		vex[i]->RN_cnt = 0;
		vex[i]->is_visited = 0;
		vex[i]->data = 0;
	}
	delete[]vex;
	vex_num = 0;
	std::cout << "内存释放完毕!" << std::endl;
}

insert

void Graph::insert(Data data){
	++vex_num;
	int** tmp = new int* [vex_num]; for (int i = 0; i < vex_num; i++) tmp[i] = new int[vex_num];
	for (int i = 0; i < vex_num; i++) for (int j = 0; j < vex_num; j++) {
		if (i == vex_num - 1 || j == vex_num - 1) tmp[i][j] = 0;
		else tmp[i][j] = GArray[i][j];
	}
	if (vex_num != 1) {	//不要释放空
		for (int i = 0; i < vex_num - 1; i++)delete[] GArray[i];
		delete []GArray;
	}
	vex[vex_num - 1]->data = data;
	std::cout << "new vertex ID: " << vex_num - 1 << "(start from zero)" << std::endl;
	GArray = tmp;
}

setdata

void Graph::setdata(int id, Data data){
	vex[id]->data = data;
}

link

void Graph::link(int id1, int id2, int weight ){
	switch (GKind){
	case UN_GRAPH: {//无向图
		if (id1 == id2) makeneighbour(id1, id2);
		else{
			makeneighbour(id1, id2);
			makeneighbour(id2, id1);
		}
		break;
	}
	case UN_NET: {//无向网
		if(id1==id2) makeneighbour(id1, id2, weight);
		else {
			makeneighbour(id1, id2, weight);
			makeneighbour(id2, id1, weight);
		}
		break;
	}
	case _GRAPH: {//有向图
		makeneighbour(id1, id2);
		break;
	}
	case _NET: {//有向网
		makeneighbour(id1, id2, weight);
		break;
	}
	default:std::cout << "Link failed!" << std::endl; break;
	}
}
void Graph::CreateArray(){
	switch (GKind)
	{
	case UN_GRAPH:
	case UN_NET: {
		for (int i = 0; i < vex_num; i++) {
			for (int j = 0; j <= i; j++) {
				if (GArray[i][j] != GArray[j][i]) {
					std::cout << "传入的矩阵作为无向图/网不合法!输入Y/N选择兼容或退出:" << std::endl;
					char c = getchar();
					switch (c) {
					default:exit(-1);
					case 'y':case 'Y': {
						link(i, j, GArray[i][j] > GArray[j][i] ? GArray[i][j] : GArray[j][i]);
						continue;
					}
					}
				}
				if (GArray[i][j] != 0 ) link(i, j, GArray[i][j]);
			}
		}
		break;
	}
	case _GRAPH:
	case _NET: {
		for (int i = 0; i < vex_num; i++) {
			for (int j = 0; j < vex_num; j++) {
				if (GArray[i][j] != 0)link(i, j, GArray[i][j]);
			}
		}
		break;
	}
	default: std::cout << "Summon From Array Failed!" << std::endl;
	}
}
void Graph::makeneighbour(int id1, int id2, int weight ) {
	//id1 出度+1。
	pNBnode* fresh = new pNBnode[vex[id1]->RN_cnt + 1];
	for (int i = 0; i < vex[id1]->RN_cnt + 1; i++) fresh[i] = new NBnode;
	for (int i = 0; i < vex[id1]->RN_cnt; i++) {
		fresh[i]->id = vex[id1]->RNeighbours[i]->id;
		fresh[i]->weight = vex[id1]->RNeighbours[i]->weight;
	}
	fresh[vex[id1]->RN_cnt]->id = id2;
	fresh[vex[id1]->RN_cnt]->weight = weight;
	for (int i = 0; i < vex[id1]->RN_cnt; i++) delete[] vex[id1]->RNeighbours[i];
	delete[] vex[id1]->RNeighbours;
	vex[id1]->RNeighbours = fresh;
	
	//id2 入度+1。
	pNBnode* fresh2 = new pNBnode[vex[id2]->LN_cnt + 1];
	for (int i = 0; i < vex[id2]->LN_cnt + 1; i++) fresh2[i] = new NBnode;
	for (int i = 0; i < vex[id2]->LN_cnt; i++) {
		fresh2[i]->id = vex[id2]->LNeighbours[i]->id;
		fresh2[i]->weight = vex[id2]->LNeighbours[i]->weight;
	}
	fresh2[vex[id2]->LN_cnt]->id = id1;
	fresh2[vex[id2]->LN_cnt]->weight = weight;
	for (int i = 0; i < vex[id2]->LN_cnt; i++) delete[] vex[id2]->LNeighbours[i];
	delete[] vex[id2]->LNeighbours;
	vex[id2]->LNeighbours = fresh2;
	
	//别忘了对邻接矩阵和用于记录的数据进行修改
	GArray[id1][id2] = weight;
	vex[id1]->RN_cnt++;
	vex[id2]->LN_cnt++;
}
void Graph::printNBS(int id){
	pGnode obj = vex[id];
	switch (GKind)
	{
	case UN_GRAPH: {
		std::cout << "顶点ID:" << id << "的邻居ID:" << std::endl;
		if (obj->RN_cnt != 0)
			for (int i = 0; i < obj->RN_cnt; i++)
				std::cout << obj->RNeighbours[i]->id << " ";
		std::cout << std::endl;
		break;
	}
	case UN_NET: {
		if (obj->LN_cnt != 0)
			std::cout << "顶点ID:" << id << "的邻居ID[权重]:" << std::endl;
		for (int i = 0; i < obj->LN_cnt; i++)
			std::cout << obj->LNeighbours[i]->id << "[" << obj->LNeighbours[1]->weight << "]" << " ";
		std::cout << std::endl;
		break;
	}
	case _GRAPH: {
		std::cout << "顶点ID:" << id << std::endl;
		if (obj->LN_cnt != 0) {
			std::cout << "左邻ID:" << std::endl;
			for (int i = 0; i < obj->LN_cnt; i++)
				std::cout << obj->LNeighbours[i]->id << " ";
			std::cout << std::endl;
		}
		if (obj->RN_cnt != 0) {
			std::cout<< "\n右邻ID:" << std::endl;
			for (int i = 0; i < obj->RN_cnt; i++)
				std::cout << obj->RNeighbours[i]->id << " ";
			std::cout << std::endl;
		}
		break;
	}
	case _NET: {
		std::cout << "顶点ID:" << id << std::endl;
		if (obj->LN_cnt != 0) {
			std::cout << "左邻ID[权重]:" << std::endl;
			for (int i = 0; i < obj->LN_cnt; i++)
				std::cout << obj->LNeighbours[i]->id <<
				"[" << obj->LNeighbours[1]->weight << "]" << " ";
			std::cout << std::endl;
		}
		if (obj->RN_cnt != 0) {
			std::cout << "右邻ID[权重]:" << std::endl;
			for (int i = 0; i < obj->RN_cnt; i++)
				std::cout << obj->RNeighbours[i]->id <<
				"[" << obj->RNeighbours[1]->weight << "]" << " ";
			std::cout << std::endl;
		}
		break;
	}
	default:
		std::cout << "Find Neighbours Error!" << std::endl;
	}
}
void Graph::printARR(){
	std::cout << "图的邻接矩阵:\n";
	for (int i = 0; i < vex_num; i++){
		for (int j = 0; j < vex_num; j++) {
			std::cout << GArray[i][j] << " ";
		}
		std::cout << std::endl;
	}
}
void Graph::DFS(int firstid){
	SetFlag();
	DFSearch(firstid);
	for (int i = 0; i < vex_num; i++) {
		if (vex[i]->is_visited == 0)DFSearch(i);
	}
	std::cout << std::endl;
}
void Graph::BFS(int firstid){	
	SetFlag();
	BFSearch(firstid); 
	for (int i = 0; i < vex_num; i++) {
		if (vex[i]->is_visited == 0)BFSearch(i);
	}
	std::cout << std::endl;
}
void Graph::SetFlag(){
	for (int i = 0; i < vex_num; i++) vex[i]->is_visited = 0;
}
void Graph::DFSearch(int firstid){
	if (vex[firstid]->is_visited == 0) {	//如果元素没被访问
		vex[firstid]->is_visited = 1;		//进行访问
		std::cout << vex[firstid]->data << " ";
		for (int i = 0; i < vex[firstid]->RN_cnt; i++) {//对这个元素所有右邻
			if (vex[vex[firstid]->RNeighbours[i]->id]->is_visited == 0) {	//如果没被访问
				DFSearch(vex[firstid]->RNeighbours[i]->id);			//进行访问
			}
		}
	}
}
void Graph::BFSearch(int firstid){
	std::queue<int> q;		    //一个队列
	q.push(firstid);		        //放入一个元素
	vex[firstid]->is_visited = 1;	//标记为访问过
	while (!q.empty()) {	    //当队列非空的时候
		for (int i = 0; i < vex[q.front()]->RN_cnt; i++) {		//对队头元素所有的右邻
			int nextid = vex[q.front()]->RNeighbours[i]->id;	//拿到右邻的id
			if (vex[nextid]->is_visited == 0) {				//如果没有被访问
				q.push(nextid);						//入队,标记为访问过
				vex[nextid]->is_visited = 1;
			}									
			else continue;
		}
		std::cout << vex[q.front()]->data << " ";
		q.pop();	//删除队头元素
	}
}

4.1.3 源文件main.cpp中,内容如下:

#include"Graph.h"
int main() {
	Graph gr(8, 0);
	gr.link(0, 1);
	gr.link(1, 2);
	gr.link(1, 7);
	gr.link(1, 4);
	gr.link(2, 3);
	gr.link(2, 5);
	gr.link(3, 6);
	for (int i = 0; i < 8; i++) {
		gr.setdata(i, 'A' + i);
	}
	for (int i = 0; i < 8; i++) {
		gr.printNBS(i);
	}
	gr.printARR();
	std::cout << "深度优先遍历" << std::endl;
	gr.DFS();
	std::cout << "广度优先遍历" << std::endl;
	gr.BFS();

	std::cout << "**************************" << std::endl;
	int** arr_for_summon = new int* [8]; for (int i = 0; i < 8; i++)arr_for_summon[i] = new int[8];
	int arr[8][8] = {
		0, 1, 0, 0, 0, 0, 0, 0,
		1, 0, 1, 0, 1, 0, 0, 1,
		0, 1, 0, 1, 0, 1, 0, 0,
		0, 0, 1, 0, 0, 0, 1, 0,
		0, 1, 0, 0, 0, 0, 0, 0,
		0, 0, 1, 0, 0, 0, 0, 0,
		0, 0, 0, 1, 0, 0, 0, 0,
		0, 1, 0, 0, 0, 0, 0, 1 };
	for (int i = 0; i < 8; i++)for (int j = 0; j < 8; j++)arr_for_summon[i][j] = arr[i][j];
	Graph gr1(arr_for_summon, 8, 0);

	for (int i = 0; i < 8; i++) {
		gr1.setdata(i, 'A' + i);
	}
	for (int i = 0; i < 8; i++) {
		gr1.printNBS(i);
	}
	gr1.printARR();
	std::cout << "深度优先遍历" << std::endl;
	gr1.DFS();
	std::cout << "广度优先遍历" << std::endl;
	gr1.BFS();
}

4.2 实验结果部分

本次实验展示以无向图为例:
第一张截图是通过link接口实现的图的构造结果展示,
第二张截图是通过传入构造矩阵创建的图的构造结果展示。
第三章截图是当我们构造无向图时,传入的矩阵不是无向图的兼容性提醒,选择y进行兼容处理,其他则退出程序,由于输入矩阵里,有两个非对称点,故提醒了两次不合法。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.上机体会

本次实验实现了图这个大类,在基础实验要求上多了有向图和网的操作,做实验时,需要从下面4个方面入手。
1、理解图的基本概念:在开始实验之前,需要先理解图的基本概念,如顶点、边、有向图、无向图等。这有助于更好地理解图的数据结构和算法,在本次实验中,虽然性质略有不同,但经过简单的处理,可以将四种结构的要求统一到一起,通过一个类来实现。
2、选择合适的数据结构:图的数据结构有多种,如邻接矩阵、邻接表、邻接多重表等。在选择数据结构时,需要根据实际情况考虑,如图的大小、是否需要动态修改等,本次实验使用邻接表和邻接矩阵的方式进行了简单的实现,如果需要,可以再声明一个存放所有节点邻接表地址的数组,形成邻接多重表,进一步化简代码。
3、实现基本操作:实现图的基本操作,如添加顶点、添加边、构造邻接表、构造邻接矩阵。删除顶点、删除边的删除操作尚未完成,但实现也并不复杂。在实现这些操作时,需要注意算法的时间复杂度和空间复杂度,以确保算法的效率,故多采用递归算法、数组存储结构。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值