图的遍历: 从给定图中指定的顶点出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使得每个顶点只会被访问一次,这个过程叫做图的遍历。图的遍历方法有两种:深度优先遍历(DFS)和广度优先遍历(BFS)。
1.深度优先遍历
深度优先遍历类似于树的先序遍历。一次深度优先遍历的基本过程可以用递归的方法来描述:
(1)从起始点v出发,首先访问顶点v。
(2)选择一个与顶点v相邻接且没有被访问过的顶点w作为新的起始点,继续深度优先遍历,直到顶点v的所有邻接点都被访问过。
例如,对于无向图来说,如果无向图是连通的,则一次深度优先遍历就可以访问到图中的所有顶点;但如果无向图是非连通的,则一次深度优先遍历只能访问到起始顶点所在连通分量的所有顶点。因此只有从其它连通分量中选择起始顶点,继续深度优先遍历,才能将所有顶点都访问一遍。
图的顶点之间的关系相对复杂,某个顶点vi既可以是其它顶点vj的邻接点,也可以是其它顶点诸如vk的邻接点。因此为了保证在遍历中每个顶点只会被访问一次,需要借助一个辅助数组visit[]
来做标记。若visit[i]
的值为true,则表示顶点vi已经被访问过;反之,则vi没有被访问过。
//图的深度优先遍历
void ALGraph::DFS_Visit() {
int i;
for (i = 0; i < vexnum; i++)
visit[i] = false; //辅助数组初始化
for (i = 0; i < vexnum; i++) {
if (visit[i] == false)
DFS(i); //对每个没有被访问过的顶点调用一次深度优先遍历
}
}
//以v为起始点的深度优先遍历
void ALGraph::DFS(int v) {
ArcNode* p;
visit[v] = true; //将起始顶点标记为已被访问的状态
cout << vexnode[v].data; //输出起始顶点的信息
p = vexnode[v].firstarc; //指针p指向起始点的第一个邻接点
while (p) {
//遍历起始顶点的所有邻接点,若邻接点已经被访问过,那么p将继续向后遍历
//否则以当前邻接点为新的起始点递归进行深度优先遍历
if (visit[p->adjvex] == false)
DFS(p->adjvex);
p = p->nextarc;
}
}
2.广度优先遍历
广度优先遍历类似于树的层次遍历。一次广度优先遍历的基本过程如下:
(1)从起始点v出发,首先访问顶点v;
(2)依次访问v的所有没有被访问的邻接点w1,w2,…,wi;
(3)按照w1,w2,…,wi的次序访问它们各自所有未被访问过的邻接点。
(4)以此类推,直到图中所有顶点v连通的顶点都被访问过为止。
同样,一次广度优先遍历只能保证将与起始点连通的顶点访问到,而其他不与起始点连通的顶点是访问不到的,因此为了保证图中所有顶点被访问到,需要对每个未被访问的顶点进行一次广度优先遍历;因此还是需要辅助数组visit[]
。
由于广度优先遍历是一种一层一层向外推进的遍历算法,所以需要使用队列进行辅助。
//图的广度优先
void ALGraph::BFS_Visit() {
int i;
for (i = 0; i < vexnum; i++)
visit[i] = false; //辅助数组从初始化,每个顶点均未被访问过
for (i = 0; i < vexnum; i++) {
if (visit[i] == false)
BFS(i);
}
}
//以v为起始点的广度优先遍历
void ALGraph::BFS(int v) {
ArcNode* p;
int Qu[MAX_V]; //定义队列
int front, rear, i;
front = rear = 0; //队列初始化
Qu[rear++] = v; //起始点入队
visit[v] = true; //入队的顶点标记为已被访问状态,防止重复入队
while (front != rear) {
i = Qu[front]; //队头元素出队
front = (front + 1) % MAX_V;
cout << vexnode[i].data; //访问队头元素
p = vexnode[i].firstarc; //指针p指向当前队头顶点的第一个结点
while (p) {
//遍历当前队头顶点的所有邻接点
if (visit[p->adjvex] == false) {
Qu[rear] = p->adjvex;
rear = (rear + 1) % MAX_V;
visit[p->adjvex] = true;
}
p = p->nextarc;
}
}
}