主要内容
图的遍历算法是实现图的其他算法的基础,图的遍历方式有两种:深度优先搜索遍历和广度优先搜索遍历。
深度优先搜索遍历类似于树的先序遍历(根节点→左子树→右子树),借助了栈结构以实现递归;广度优先搜索遍历则类似于层次遍历,借助于队列结构实现。
因为图的任意两个顶点都有可能存在联系,所以在访问某个顶点后,可能沿着某条路径又会回到访问过的顶点。为了避免同一顶点被访问多次,在遍历图的过程中,必须用一个数组visited[vexnum]记录每个访问过的顶点。
以邻接矩阵为存储结构的遍历,时间复杂度为O(n²);以邻接表为存储结构的遍历,时间复杂度为O(n+e)。
广度优先搜索
广度优先搜索(Breadth First Search,BFS)遍历类似于树的层次遍历。
广度优先的过程与深度优先类似,唯一的区别是广度优先是一层一层地遍历,而不会从某个顶点开始一直往下遍历,直到不符合遍历的条件。
广度优先以横向遍历为先,即访问顶点v1后,顺序访问v1的邻接顶点v2,v3,然后先顺序访问v2的邻接顶点,再顺序访问v3的邻接顶点,如此类推。
深度优先利用栈结构实现递归,广度优先则需要队列来实现每一层的顺序执行:
(1)v1入队,访问顶点v1,并在visited[vexnum]将顶点v1对应的元素值归TRUE;
(2)检查v1的邻接顶点,v2和v3顺序入队;
(3)v1出队;
(4)访问顶点v2,检查v2的邻接顶点并使它们顺序入队;
(5)v2出队,访问顶点v3;
(6)访问顶点v3,检查v3的邻接顶点并使它们顺序入队;
(7)之后的入队、出队过程如此类推。
*利用队列“先来先服务”(FCFS)的性质,在编写代码时可以将思路转化为先求队列,再遍历。
以邻接矩阵为存储结构的广度优先搜索遍历
int visited[G.vexnum]; /*全局变量和静态变量初始化时会自动被设置为0*/
LinkQueue Q; /*定义链队Q*/
InitQueue(Q); /*初始化链队Q*/
void AL_EnQueue(LinkQueue &Q, int v1) /*从顶点v1开始,将所有顶点逐个入队*/
{
int i = LocateVex(G, v1);
for(int v2 = 1; v2 <= G.vexnum; v2++) /*先使v1未被访问的邻接顶点顺序入队*/
{
int j = LocateVex(G, v2);
if(G.arcs[i][j] != 0 && visited[j] = FALSE)
{
EnQueue(Q, v2);
visited[j] = TRUE; /*标记*/
}
}
for(int v2 = 1; v2 <= G.vexnum; v2++) /*再在v1的邻接顶点上递归*/
{
int j = LocateVex(G, v2);
if(G.arcs[i][j] != 0 && visited[j] = FALSE)
AL_QnQueue(Q, v2);
}
}
void BFS_AMG(AMGraph &G, int v1)
{
EnQueue(Q, v1); /*v1入队*/
int i = LocateVex(G, v1);
visited[i] = TRUE;
AL_EnQueue(Q, v1); /*所有顶点入队*/
cout<<GetHead(Q); /*访问链队的头结点*/
DeQueue(Q); /*头结点出队*/
}
以邻接表为存储结构的广度优先搜索遍历
int visited[G.vexnum];
LinkQueue Q;
InitQueue(Q);
void AL_EnQueue(LinkQueue &Q, int v1)
{
ArcNode *p = G.ALs[v1-1].nextarcnode;
while(p != NULL)
{
int v2 = p->anothervex;
if(visited[v2-1] = FALSE)
EnQueue(Q, v2);
p = p->nextarcnode;
}
while(p != NULL)
{
int v2 = p->anothervex;
if(visited[v2-1] = FALSE)
AL_EnQueue(Q, v2);
p = p->nextarcnode;
}
}
void BFS_AMG(AMGraph &G, int v1)
{
EnQueue(Q, v1);
visited[v1-1] = TRUE;
AL_EnQueue(Q, v1);
cout<<GetHead(Q);
DeQueue(Q);
}
由深度优先的遍历过程可以得到一棵以起始顶点v1为根结点的树,这棵树称作深度优先生成树。