定义:
图就是由一些小圆点(成为顶点)和连接这些小圆点的直线(称为边)组成的。例如上图是由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;
}