图 = =

定义:

图 = =

图就是由一些小圆点(成为顶点)和连接这些小圆点的直线(称为边)组成的。例如上图是由5个顶点(编号为1、2、3、4、5)和5条边(1-2、1-3、1-5、2-4、3-5)组成。

遍历:

现在我们从1号顶点开始遍历这个图,遍历就是指把图的每一个顶点都访问一次。

使用深度优先搜索来遍历这个图将会得到如下的结果。

图 = =

这五个顶点的被访问顺序如下图。图中每个顶点右上方的数就表示这个顶点是第几个被访问到的,我们还为这个数起了很好听的名字---时间戳。

图 = =

 

图的存储:

最常用的方法:使用一个二维数组e来存储,如下:

图 = =

上图二维数组第i行第j列表示的点就是顶点i到顶点j是否有边。 1表示有边,oo表示没有边,这里我们将自己到自己(即i等于j)设为0。我们将这种存储图的方法成为图的邻接矩阵存储法。

上面的二维数组是沿主对角线(左上角到右下角)对称的,因为上面这个图是无向图。所谓无向图指的是图的边没有方向,例如上图1-5表示,1号顶点可以到5号顶点,5号顶点也可以到1号顶点。

使用深度优先搜索遍历:

图 = =

过程具体是:首先从一个未走到过的顶点作为起始顶点,比如以1号顶点作为起点。沿1号顶点的边去尝试访问其它未走到的顶点,首先发现2号顶点还没有走到过,于是来到了2号顶点。再以2号顶点作为出发点继续尝试访问其它未走到过的顶点,这样又来到了4号顶点。再以4号顶点作为出发点继续尝试访问其它未走到过的顶点。

但是,此时沿4号顶点的边,已经不能访问到其它未走到过的顶点了,所以需要返回到2号顶点。因此还需要继续返回到1号顶点。再继续沿1号顶点的边看看还能否访问到其它未走到过的顶点。此时又会来到3号顶点,再以3号顶点作为出发点继续访问其它未走过的顶点,于是又来到5号顶点。

到此,所有顶点都走到过了,遍历结束。

深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点,直到所有的顶点都被访问过。显然,深度优先遍历是沿着图的某一条分支遍历直至末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止。

实操代码:

#include<bits/stdc++.h>

using namespace std;

int e[101][101]; // 二维数组e存储的就是图的边(邻接矩阵)
int book[101]; // 数组book用来记录哪些顶点已经访问过
int sum; // sum用来记录已经访问过多少个顶点
int n; // n存储的是图的顶点的总个数 

void dfs(int cur) // cur是当前所在的顶点编号(正在遍历的顶点)
{
	int i;
	cout << cur << ' ';
	sum ++; // 每访问一个顶点,sum就加1
	if(sum == n)  return;  //所有的顶点都已经访问过则直接退出
	for(i = 1 ; i <= n ; i ++) // 从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连 
    {
    	// 判断当前顶点cur到顶点i是否有边,并判断顶点i是否访问过
		if(e[cur][i] == 1 && book[i] == 0)
		{
			book[i] = 1; // 标记顶点i已经访问过
			dfs(i); // 从顶点i再出发继续遍历 
		} 
	}
	return; 
}   

int main()
{
	int i,j;
	int m;
	int a,b;
	cin >> n >> m;
	
	//初始化二维矩阵
	for(i = 1 ; i <= n ; i ++)
	{
		for(j = 1 ; j <= m ; j ++)
		{
			if(i == j)  e[i][j] = 0;
			else  e[i][j] = 999999999;  // 我们这里假设999999999为正无穷 
		}
	} 
	
	//读入顶点之间的边
	for(i = 1 ; i <= m ; i ++)
	{
		cin >> a >> b;
		e[a][b] = 1;
		e[b][a] = 1; // 这里是无向图,所以需要将e[b][a]也赋为1 
	} 
	
	//从1号城市出发
	book[1] = 1; // 标记1号顶点已访问
	dfs(1); // 从1号顶点开始遍历
	
	getchar(); getchar();
	
	return 0; 
}

使用广度优先搜索来遍历:

(1)结果

图 = =

(2)顶点被访问的顺序

图 = =

(3)遍历过程

首先以一个未被访问过的顶点作为起始顶点,比如以1号顶点作为起点。将1号顶点放入队列中,然后将与1号顶点相邻的未访问过的顶点即2号、3号和5号顶点依次再放入到队列中。如下图:

图 = =

接下来再将2号顶点相邻的未访问过的顶点4号顶点放入队列中。到此所有顶点都被访问过,遍历结束。如下图:

图 = =

广度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。

实操代码:

#include<bits/stdc++.h>

using namespace std;

int book[101];
int e[101][101];
int que[10001],head,tail;

int main()
{
	int i,j;
	int n,m;
	int a,b;
	int cur;
	
	cin >> n >> m;
	
	//初始化二维矩阵 
	for(i = 1 ; i <= n ; i ++)
	{
		for(j = 1 ; j <= n ; j ++)
		{
			if(i == j)  e[i][j] = 0;
			else  e[i][j] == 999999999; // 我们这里假设99999999为无穷 
		}
	}
	
	//读入顶点之间的边
	for(i = 1 ; i <= m ; i ++)
	{
		cin >> a >> b; 
		e[a][b] = 1;
		e[b][a] = 1; // 这里是无向图,所以需要将e[b][a]也赋值为1 
	} 
	
	//队列初始化
	head = 1,tail = 1;
	
	//从1号顶点出发,将1号顶点加入队列
	que[tail] = 1;
	tail ++;
	book[1] = 1; // 标记1号顶点已访问
	
	//当队列不为空
	while(head < tail)
	{
		cur = que[head]; // 当前正在访问的顶点编号
		for(i = 1 ; i <= n ; i ++) // 从1~n依次尝试
		{
			//判断从顶点cur到顶点i是否有边,并判断顶点i是否已经访问过
			if(e[cur][i] == 1 && book[i] == 0)
			{
				// 如果从顶点cur到顶点i有边,并且顶点i没有被访问过,则将顶点i入队
				que[tail] = i;
				tail ++;
				book[i] = 1; // 标记顶点i已访问 
			} 
			
			//如果tail > n , 则表明所有顶点都已经被访问过
			if(tail > n)  break; 
		} 
		head ++; // 注意这地方,千万不要忘记当一个顶点扩展结束后,head ++ ,然后才能继续往下扩展 
	}
	 
	for(i = 1 ; i < tail ; i ++)
	{
		cout << que[i] << ' ';
	}
	
	getchar();getchar();
	
	return 0; 
	
}

 

上一篇:第3项:用私有构造器或者枚举类型强化Singleton属性


下一篇:2021第十二届蓝桥杯省赛c++B组_卡片