北邮22信通:第五章 邻接表图的广度遍历和深度遍历

news2024/11/24 6:44:35

北邮22信通一枚~   

跟随课程进度每周更新数据结构与算法的代码和文章 

持续关注作者  解锁更多邮苑信通专属代码~

获取更多文章  请访问专栏:

北邮22信通_青山如墨雨如画的博客-CSDN博客

目录

一.总纲

二.构造函数

2.1构造函数讲解

2.2构造函数的书写

书上代码如下:

小编改进后的代码

 三.深度优先遍历DFS

3.1书上重要语句提取:

3.2一个修饰

3.3 DFS完整代码

四.广度优先遍历BFS

4.1广度优先遍历注意事项

4.2广度优先遍历代码部分

4.2.1使用数组拟合队列描写BFS

4.2.2直接使用STL队列描写BFS

五.析构函数

5.1核心思路

5.2代码部分

六.总体代码

6.1代码效果图

6.2代码部分

6.3运行结果


 

一.总纲

***说明***

1.本篇文章使用邻接表的存储结构实现图,将分别介绍广度优先遍历深度优先遍历两种图的遍历方法;

2.由于书中代码存在欠缺之处,本代码对书中代码做了修改、改进和修饰,具体将在后文讲解;

3.本篇文章测试用图为书169页图5-25(a)所呈现的图,广度优先遍历和深度优先遍历部分代码将对这张图进行测试。

 

***说明完毕***

二.构造函数

2.1构造函数讲解

图的邻接表存储结构及其含义如下:

这里标一下英文单词及其释义:

vertex:顶点

arc:弧

        图的构造实际上就是讲一张图的所有数据信息数字化。如何把一张图用最简洁的数字组合描述出来呢?

        首先我们应该获取这张图结点个数弧的个数

        对所有结点,我们使用一个数组(adjlist)将他们存储起来。如果用数组存储所有结点,那么每个结点所承载的数据,就变成了两个,一个是结点本身的数据域(传入为temp类型,主函数中具象化为student),另一个是它在数组中的位置(下标)

        对每条弧,显然,只要知道这条弧的起始顶点和指向顶点,就可以清晰且唯一地描述这条弧了。所以,我们需要传入所有弧的开始和指向结点的数据。通过上面分析,我们知道一个结点承载两个数据,那使用哪个数据会更加简洁呢?显然,传入数据应该使用数组下标

        综上分析,我们需要的数据有这些:结点个数,弧的个数,结点储存的temp类型数据,弧的首尾结点的数组下标。

        我们通过上图所画出的邻接表来具体说明各种数据。

        则对构造函数,首先传入结点个数和弧的个数;然后传入结点数据域存放的temp类型的数据,可以用传入temp类型数组来实现; 然后传入弧的数字化描述,具体需要传入数对,数对可以用结构体来整理,即传入整型结构体数组来实现对弧的数字化描述

 2.2一个修改和改进

        书中代码对弧数据的传入采用的是头插法的方法,但是需要逆序输入数对才可以正确运行。修改方法一是增加一个形参,以结构体数组的形式逆序遍历传入数对,同时赋值和储存;修改方法二就是直接逆序输入,但是过程麻烦。

        因为在大家普遍学习的C++课程中对文件的读写要求不高,所以小编将构造函数改成了传入正常参数的方式,这样也能同时使用上述提供的解决方法二,轻松地将弧数据输入到程序中。

2.2构造函数的书写

书上代码如下:

template<class temp>
al_graph<temp>::al_graph(ifstream& fin)
{
	fin >> this->vnum;
	fin >> this->arcnum;
	for (int i = 0; i < vnum; i++)
	{
		fin >> this->adjlist[i].vertex;
		this->adjlist[i].first_arc = nullptr;
	}
	for (int i = 0; i < arcnum; i++)
	{
		int i, j;
		fin >> i >> j;//依次输入本初结点,邻接顶点
		arc_node* s = new arc_node;
		s->adj_vertex = j;//j是邻接顶点的下标
		s->next_arc = this->adjlist[i].first_arc;
		this->adjlist[i].first_arc = s;
	}
}

小编改进后的代码

(其中relation数组即为传入的整型结构体形参,用来数字化弧)

template<class temp>
al_graph<temp>::al_graph(temp a[], relation r[], int vnum, int arcnum)
{
	this->vnum = vnum;
	this->arcnum = arcnum;
	for (int i = 0; i < vnum; i++)
	{
		this->adjlist[i].vertex = a[i];
		this->adjlist[i].first_arc = nullptr;
	}
	for (int i = arcnum - 1; i >= 0; i--)//逆序输入
	{
		arc_node* s = new arc_node;
		s->adj_vertex = r[i].end;
		s->next_arc = this->adjlist[r[i].start].first_arc;
		this->adjlist[r[i].start].first_arc = s;
	}
}

 三.深度优先遍历DFS

3.1书上重要语句提取:

“为避免重复访问同一个顶点,必须记住每个顶点是否已经被访问过,所以,图的遍历算法必须添加一个布尔向量bool visited[n],初始值为FALSE,一旦访问了顶点vi,则visited[ i - 1 ]设置为TRUE。”

“深度优先遍历类似于树的前序遍历。”

3.2一个修饰

书上代码固然可以实现深度优先遍历,但是遍历一次之后数组visited各个元素都将被标记,无法进行第二次深度遍历。所以在这里小编添加了一个return0函数,方便将各个visited数组全部返回FALSE值。

template<class temp>
void al_graph<temp>::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

使用完DFS方法后引用此函数,就可以实现visited数组的格式化了。

3.3 DFS完整代码

DFS完整代码如下:

template<class temp>
void al_graph<temp>::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

bool DFS_visited[maxsize];
template<class temp>
void al_graph<temp>::DFS_core(int v)
{
	this->adjlist[v].vertex.print();
	DFS_visited[v] = 1;
	arc_node* p = this->adjlist[v].first_arc;
	while (p != NULL)
	{
		int j = p->adj_vertex;
		if (DFS_visited[j] == 0)
			DFS_core(j);
		p = p->next_arc;
	}
}

template<class temp>
void al_graph<temp>::DFS(int v)
{
	cout << "从"<<v+1<<"开始,深度优先遍历结果为:" << endl;
	this->DFS_core(v);
	return0(DFS_visited);
	cout << endl;
}

四.广度优先遍历BFS

4.1广度优先遍历注意事项

同样需要visited数组来记录是否标记过;

应用了队列思想,每一个元素出队,与这个元素直接相邻的元素依次入队,直至队空为止;

广度优先遍历类似于树的层序遍历

4.2广度优先遍历代码部分

4.2.1使用数组拟合队列描写BFS

template<class temp>
void al_graph<temp>::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

bool BFS_visited[maxsize];
template<class temp>
void al_graph<temp>::BFS_core(int v)
{
	int queue[maxsize];
	int front = 0, rear = 0;
	this->adjlist[v].vertex.print();
	BFS_visited[v] = 1;
	queue[++rear] = v;
	while (front != rear)
	{
		v = queue[++front];
		arc_node* p = adjlist[v].first_arc;
		while (p != nullptr)
		{
			int j = p->adj_vertex;
			if (BFS_visited[j] == 0)
			{
				this->adjlist[j].vertex.print();
				BFS_visited[j] = 1;
				queue[++rear] = j;
			}
			p = p->next_arc;
		}
	}
}

template<class temp>
void al_graph<temp>::BFS(int v)
{
	cout << "从" << v + 1 << "开始,广度优先遍历的结果为:" << endl;
	BFS_core(v);
	return0(BFS_visited);
	cout << endl;
}

4.2.2直接使用STL队列描写BFS

template<class temp>
void al_graph<temp>::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

template<class temp>
void al_graph<temp>::BFS_core_for_queue(int v)
{
	queue<int>q;
	this->adjlist[v].vertex.print();
	BFS_visited[v] = 1;
	q.push(v);
	while (!q.empty())
	{
		v = q.front();
		q.pop();
		arc_node* p = adjlist[v].first_arc;
		while (p != nullptr)
		{
			int j = p->adj_vertex;
			if (BFS_visited[j] == 0)
			{
				this->adjlist[j].vertex.print();
				BFS_visited[j] = 1;
				q.push(j);
			}
			p = p->next_arc;
		}
	}
}

template<class temp>
void al_graph<temp>::BFS_for_queue(int v)
{
	cout << "从" << v + 1 << "开始,利用队列存储,广度优先遍历结果为:" << endl;
	BFS_core_for_queue(v);
	return0(BFS_visited);
	cout << endl;
}

五.析构函数

因为邻接表存储结构中链表数量不单一,所以单独拿出来讲一下。

5.1核心思路

按顺序析构。因为有adjlist数组,按照下标依次对每一条链表析构。

5.2代码部分

template<class temp>
al_graph<temp>::~al_graph()
{
	int i = 0;
	while (i < this->vnum)
	{
		arc_node* p = this->adjlist[i++].first_arc;
		while (p != nullptr)
		{
			arc_node* q = p->next_arc;
			delete p;
			p = q;
		}
	}
}

六.总体代码

6.1代码效果图

6.2代码部分

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

class student
{
private:
	int ID;
	string name;
public:
	student()
	{
		this->ID = 0;
		this->name = "un_known name";
	}
	student(int ID, string name)
	{
		this->ID = ID;
		this->name = name;
	}
	void print()
	{
		cout << "ID:" << this->ID << "  name:" << this->name << endl;
	}
	friend fstream& operator>>(fstream& fin, student& s)
	{
		fin >> s.ID;
		fin >> s.name;
		return fin;
	}
};

struct arc_node
{
	int adj_vertex;//数据域 邻接顶点的下标
	arc_node* next_arc;//指针域 指向下一条弧结点
};

template<class temp>
struct vertex_node
{
	temp vertex;//数据域 顶点信息
	arc_node* first_arc;//指针域 指向第一条弧
};

struct relation//整型结构体数组,用来传递弧数据
{
	int start;
	int end;
};

const int maxsize = 10;
template<class temp>
class al_graph
{
private:
	vertex_node<temp> adjlist[maxsize];
	int vnum, arcnum;
public:
	al_graph(ifstream& fin);
	al_graph(temp a[],relation r[],int vnunm,int arcnum);

	void return0(bool x[]);

	void DFS_core(int v);
	void DFS(int v);

	void BFS_core(int v);
	void BFS(int v);

	void BFS_core_for_queue(int v);
	void BFS_for_queue(int v);
	~al_graph();
};

template<class temp>
al_graph<temp>::al_graph(ifstream& fin)
{
	fin >> this->vnum;
	fin >> this->arcnum;
	for (int i = 0; i < vnum; i++)
	{
		fin >> this->adjlist[i].vertex;
		this->adjlist[i].first_arc = nullptr;
	}
	for (int i = 0; i < arcnum; i++)
	{
		int i, j;
		fin >> i >> j;//依次输入本初结点,邻接顶点
		arc_node* s = new arc_node;
		s->adj_vertex = j;//j是邻接顶点的下标
		s->next_arc = this->adjlist[i].first_arc;
		this->adjlist[i].first_arc = s;
	}
}

template<class temp>
al_graph<temp>::al_graph(temp a[], 
	relation r[], int vnum, int arcnum)
{
	this->vnum = vnum;
	this->arcnum = arcnum;
	for (int i = 0; i < vnum; i++)
	{
		this->adjlist[i].vertex = a[i];
		this->adjlist[i].first_arc = nullptr;
	}
	for (int i = arcnum - 1; i >= 0; i--)
	{
		arc_node* s = new arc_node;
		s->adj_vertex = r[i].end;
		s->next_arc = this->adjlist[r[i].start].first_arc;
		this->adjlist[r[i].start].first_arc = s;
	}
}

template<class temp>
void al_graph<temp>::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

bool DFS_visited[maxsize];
template<class temp>
void al_graph<temp>::DFS_core(int v)
{
	this->adjlist[v].vertex.print();
	DFS_visited[v] = 1;
	arc_node* p = this->adjlist[v].first_arc;
	while (p != NULL)
	{
		int j = p->adj_vertex;
		if (DFS_visited[j] == 0)
			DFS_core(j);
		p = p->next_arc;
	}
}

template<class temp>
void al_graph<temp>::DFS(int v)
{
	cout << "从"<<v+1<<"开始,深度优先遍历结果为:" << endl;
	this->DFS_core(v);
	return0(DFS_visited);
	cout << endl;
}

bool BFS_visited[maxsize];
template<class temp>
void al_graph<temp>::BFS_core(int v)
{
	int queue[maxsize];
	int front = 0, rear = 0;
	this->adjlist[v].vertex.print();
	BFS_visited[v] = 1;
	queue[++rear] = v;
	while (front != rear)
	{
		v = queue[++front];
		arc_node* p = adjlist[v].first_arc;
		while (p != nullptr)
		{
			int j = p->adj_vertex;
			if (BFS_visited[j] == 0)
			{
				this->adjlist[j].vertex.print();
				BFS_visited[j] = 1;
				queue[++rear] = j;
			}
			p = p->next_arc;
		}
	}
}

template<class temp>
void al_graph<temp>::BFS(int v)
{
	cout << "从" << v + 1 << "开始,广度优先遍历的结果为:" << endl;
	BFS_core(v);
	return0(BFS_visited);
	cout << endl;
}

template<class temp>
void al_graph<temp>::BFS_core_for_queue(int v)
{
	queue<int>q;
	this->adjlist[v].vertex.print();
	BFS_visited[v] = 1;
	q.push(v);
	while (!q.empty())
	{
		v = q.front();
		q.pop();
		arc_node* p = adjlist[v].first_arc;
		while (p != nullptr)
		{
			int j = p->adj_vertex;
			if (BFS_visited[j] == 0)
			{
				this->adjlist[j].vertex.print();
				BFS_visited[j] = 1;
				q.push(j);
			}
			p = p->next_arc;
		}
	}
}

template<class temp>
void al_graph<temp>::BFS_for_queue(int v)
{
	cout << "从" << v + 1 << "开始,利用队列存储,广度优先遍历结果为:" << endl;
	BFS_core_for_queue(v);
	return0(BFS_visited);
	cout << endl;
}

template<class temp>
al_graph<temp>::~al_graph()
{
	int i = 0;
	while (i < this->vnum)
	{
		arc_node* p = this->adjlist[i++].first_arc;
		while (p != nullptr)
		{
			arc_node* q = p->next_arc;
			delete p;
			p = q;
		}
	}
}

student stu[6] = 
{ 
	{1001,"zhang"},{2002,"wang"},{3003,"li"},
	{4004,"zhao"},{5005,"liu"},{6006,"yao"}
};

//写入文本对象的构造函数因为是头插法,所以连接方式需要倒序输入
relation r1[12] =
{
	{0,1},{0,2},{0,5},
	{1,0},{1,2},{1,4},
	{2,0},{2,1},{2,3},
	{3,2},
	{4,1},
	{5,0}
};

relation r2[12] =
{
	{0,5},{0,2},{0,1},
	{1,4},{1,2},{1,0},
	{2,3},{2,1},{2,0},
	{3,2},
	{4,1},
	{5,0}
};

int main()
{
	system("color 0A");
	al_graph<student>aa(stu, r1, 6, 12);
	aa.DFS(0);
	aa.DFS(3);

	aa.BFS(0);
	aa.BFS(3);

	aa.BFS_for_queue(0);
	aa.BFS_for_queue(3);

	return 0;
}

6.3运行结果

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/828247.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

echars力引导关系图

效果图 力引导关系图 力引导布局是模拟弹簧电荷模型在每两个节点之间添加一个斥力&#xff0c;每条边的两个节点之间添加一个引力&#xff0c;每次迭代节点会在各个斥力和引力的作用下移动位置&#xff0c;多次迭代后节点会静止在一个受力平衡的位置&#xff0c;达到整个模型…

Java项目-苍穹外卖-Day01

文章目录 博客介绍软件开发流程项目介绍产品原型技术选型 博客介绍 从头开始做黑马的苍穹外卖项目 每天记录一下新学的知识点以及对应的开发进度 然后一些在项目中遇到的问题会重点标注 本篇主要是后端的&#xff0c;前端会一笔带过 软件开发流程 流程介绍需求分析需求规格说…

使用pg_prewarm缓存PostgreSQL数据库表

pg_prewarm pg_prewarm 直接利用系统缓存的代码,对操作系统发出异步prefetch请求&#xff0c;在应用中&#xff0c;尤其在OLAP的情况下&#xff0c;对于大表的分析等等是非常耗费查询的时间的&#xff0c;而即使我们使用select table的方式&#xff0c;这张表也并不可能将所有…

Eureka 学习笔记2:EurekaClient

版本 awsVersion ‘1.11.277’ EurekaClient 接口实现了 LookupService 接口&#xff0c;拥有唯一的实现类 DiscoveryClient 类。 LookupService 接口提供以下功能&#xff1a; 获取注册表根据应用名称获取应用根据实例 id 获取实例信息 public interface LookupService<…

【LeetCode】141. 环形链表 进阶题142. 环形链表 II

141. 环形链表 这道题还是用经典的快慢指针法来做。每次让快的指针走两步&#xff0c;慢的走一步。如果有环&#xff0c;则绝对会在环内的某一节点相遇。思想跟物理知识有点关系&#xff0c;如果有环&#xff0c;则在相对运动过程中&#xff0c;可以相当于慢指针静止&#xff0…

STM32入门——外部中断

中断系统概述 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行中断优先级&#xff…

Java集合数组相互转换

1.集合转换成数组 &#xff08;1&#xff09;集合a通过toArray()方法进行转换为数组&#xff0c;可以转换成为指定类型的数组&#xff0c;但是这些类型都必须是object类型的子类&#xff0c;基本类型不可以。 必要时我们使用强制类型转换&#xff0c;转成我们需要的类型。 Li…

【网络编程】poll

主旨思想 用一个结构体记录文件描述符集合&#xff0c;并记录用户态状态和内核态状态 函数说明 概览 #include <poll.h> struct pollfd { int fd; /* 委托内核检测的文件描述符 */ short events; /* 委托内核检测文件描述符的什么事件 */ short revents; /* 文件描述…

MySQL 远程操作mysql

可以让别人在他们的电脑上操作我电脑上的数据库 create user admin identified with mysql_native_password by admin; //设置账号密码都为admingrant all on *.* to admin; //给admin账号授权 授权完成

​五、驱动 - ​音频系统硬件电路

文章目录 1. 音频系统硬件电路结构2. 蓝牙音频2.1 音乐播放2.2 VoIP通话2.3 4G通话3. 其他3.1 什么是S/PDIF1. 音频系统硬件电路结构 录音放音设备:mic、speaker、耳机、听筒这些带有录音放音功能的设备(因为录放设备可能是模拟设备也可能是数字设备,所以接口可能是模拟接口…

IDEA中怎么使用git下载项目到本地,通过URL克隆项目(gitee\github)

点击 新建>来自版本控制的项目 点击后会弹出这样一个窗口 通过URL拉取项目代码 打开你要下载的项目仓库 克隆>复制 gitee github也是一样的 返回IDEA 将刚刚复制的URL粘贴进去选择合适的位置点击克隆 下载完成

JavaEE初阶之网络初识

一、网络发展史 1.1独立模式 独立模式:计算机之间相互独立; 1.2网络互连 随着时代的发展,越来越需要计算机之间互相通信,共享软件和数据,即以多个计算机协同工作来完成业务,就有了网络互连。网络互连:将多台计算机连接在一起,完成数据共享。 数据共享本质是网络数据…

【Python】5分钟了解11个最佳的Python编译器和解释器

11个最佳Python编译器和解释器 1. Brython2. Pyjs3. WinPython4. Skulpt5. Shed Skin6. Active Python7. Transcrypt8. Nutika9. Jython10. CPython11. IronPython结论原文链接 Python是一门初学者的编程语言。它是一种高级语言&#xff0c;非常灵活、解释性和面向对象的语言。…

安防监控进入全景时代,萤石全景摄像机E4p体验评测

随着智能家居的普及&#xff0c;智能家居摄像机已经成为我们必备的智能家居设备之一。传统摄像机在捕捉画面时只能获得单一角度的画面&#xff0c;可能会错过关键信息。 针对这个问题&#xff0c;萤石最近推出的E4p全景摄像机&#xff0c;解决了用户在特定场景下需要更全面画面…

笔试数据结构选填题

目录 卡特兰数Catalan&#xff1a;出栈序列/二叉树数 树 二叉树 N01N2 哈夫曼树&#xff08;最优二叉树&#xff09;Huffman 度m的哈夫曼树只有度为0和m的结点&#xff1a;Nm(n-1)/(m-1) 平衡二叉树AVL Nh表示深度为h最少结点数&#xff0c;则N00&#xff0c;N11&#…

Linux下TCP网络服务器与客户端通信程序入门

文章目录 目标服务器与客户端通信流程TCP服务器代码TCP客户端代码 目标 实现客户端连接服务器&#xff0c;通过终端窗口发送信息给服务器端&#xff0c;服务器接收到信息后对信息数据进行回传&#xff0c;客户端读取回传信息并返回。 服务器与客户端通信流程 TCP服务器代码 …

AI 绘画Stable Diffusion 研究(五)sd文生图功能详解(下)

大家好&#xff0c;我是风雨无阻。 上一篇文章详细介绍了sd文生图的功能及使用注意事项&#xff0c;感兴趣的朋友可以前往查看&#xff1a;AI 绘画Stable Diffusion 研究&#xff08;四&#xff09;sd文生图功能详解&#xff08;上&#xff09; 。 那今天这篇文章&#xff0c;我…

【牛客网】二叉搜索树与双向链表

二叉搜索树与双向链表 题目描述算法分析编程代码 链接: 二叉搜索树与双向链表 题目描述 算法分析 编程代码 /* struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;TreeNode(int x) :val(x), left(NULL), right(NULL) {} };*/ class Solution { public:…

解决Hadoop审计日志hdfs-audit.log过大的问题

【背景】 新搭建的Hadoop环境没怎么用&#xff0c;就一个环境天天空跑&#xff0c;结果今天运维告诉我说有一台服务器磁盘超过80%了&#xff0c;真是太奇怪了&#xff0c;平台上就跑了几个spark测试程序&#xff0c;哪来的数据呢&#xff1f; 【问题调查】 既然是磁盘写满了&…

第七章 图论

第七章 图论 一、数据结构定义 图的邻接矩阵存储法#define MaxVertexNum 100 // 节点数目的最大值// 无边权&#xff0c;只用0或1表示边是否存在 bool graph[MaxVertexNum][MaxVertexNum];// 有边权 int graph[MaxVertexNum][MaxVertexNum];图的邻接表存储法 把所有节点存储为…