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

北邮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)所呈现的图,广度优先遍历和深度优先遍历部分代码将对这张图进行测试。

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

 

***说明完毕***

二.构造函数

2.1构造函数讲解

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

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

vertex:顶点

arc:弧

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

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

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

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

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

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

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

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

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

 2.2一个修改和改进

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

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

2.2构造函数的书写

书上代码如下:

template
al_graph::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
al_graph::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
void al_graph::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

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

3.3 DFS完整代码

DFS完整代码如下:

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

bool DFS_visited[maxsize];
template
void al_graph::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
void al_graph::DFS(int v)
{
	cout << "从"<DFS_core(v);
	return0(DFS_visited);
	cout << endl;
}

四.广度优先遍历BFS

4.1广度优先遍历注意事项

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

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

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

4.2广度优先遍历代码部分

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

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

bool BFS_visited[maxsize];
template
void al_graph::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
void al_graph::BFS(int v)
{
	cout << "从" << v + 1 << "开始,广度优先遍历的结果为:" << endl;
	BFS_core(v);
	return0(BFS_visited);
	cout << endl;
}

4.2.2直接使用STL队列描写BFS

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

template
void al_graph::BFS_core_for_queue(int v)
{
	queueq;
	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
void al_graph::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
al_graph::~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代码效果图

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

6.2代码部分

#include
#include
#include
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
struct vertex_node
{
	temp vertex;//数据域 顶点信息
	arc_node* first_arc;//指针域 指向第一条弧
};

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

const int maxsize = 10;
template
class al_graph
{
private:
	vertex_node 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
al_graph::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
al_graph::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
void al_graph::return0(bool x[])
{
	for (int i = 0; i < maxsize; i++)
		x[i] = 0;
}

bool DFS_visited[maxsize];
template
void al_graph::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
void al_graph::DFS(int v)
{
	cout << "从"<DFS_core(v);
	return0(DFS_visited);
	cout << endl;
}

bool BFS_visited[maxsize];
template
void al_graph::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
void al_graph::BFS(int v)
{
	cout << "从" << v + 1 << "开始,广度优先遍历的结果为:" << endl;
	BFS_core(v);
	return0(BFS_visited);
	cout << endl;
}

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

template
al_graph::~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_graphaa(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运行结果

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

 

 

 

你可能感兴趣的:(北邮22信通,数据结构,深度优先,图论,算法,北邮信通,数据结构,广度优先)