图的遍历
图中的某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次。
深度优先遍历(DFS)
1. 原理
可以约定右手原则:在没有碰到重复顶点的情况下,分岔路口始终向右手方向走,每路过一个顶点就做上标记。遇到重复情况就往后退,直到退到一开始的顶点,即遍历完成。
标记的具体方法:设置一个访问数组visited[n],n是图中顶点个数,初值为0,访问过后设置为1
深度遍历其实是一个递归过程,就像是一颗树的***前序遍历***。它从图中某一个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直到图中所有和v有路径相通的顶点都被访问到。对于非连通图,只需要对它的连通分量分别进行深度优先遍历。
2.代码
邻接矩阵
typedef int Boolean; //Boolean是布尔类型,其值是TRUE或FALSE
Boolean visited[MAX]; //访问标志的数组
//邻接矩阵的深度优先递归算法
void DFS(MGraph G, int i)
{
int j;
visited[i] = TRUE;
printf("%c", G.vexs[i]);
for(j = 0; j < G.numVertexes; j++)
{
if(G.arc[i][j] == 1 && !visit[j])
{
DFS(G, j); //对为访问的邻接顶点递归调用
}
}
}
//深度遍历
void DFSTraverse(MGraph G)
{
int i;
for(i = 0; i < G.numVertexes; i++)
{
visit[i] = FALSE; //初始的状态都是未被访问
}
for(i = 0; i < G.numVertexes; i++)
{
if(!visit[i]) //对未访问过的顶点调用DFS,若是连通图,只会执行一次
{
DFS(G, i);
}
}
}
邻接表
DFSTraverse代码几乎一样,只是递归函数因为将数组换成链表而不同
//邻接表的深度遍历
void DFS(GraphAdjList GL, int i)
{
EdgeNode *p;
visited[i] = TRUE;
printf("%c", GL -> adjList[i].data);
p = GL -> adjList[i].firstedge;
while(p)
{
if(!visited[p -> adjvex])
{
DFS(GL, p -> adjvex);
}
p = p -> next;
}
}
3. 总结
邻接矩阵是二维数组,要查找每个顶点的邻接点需要访问矩阵中的所有元素,因此需要O(n ^ 2)。而邻接表取决于顶点和边的数量,所以是O(n + e)。对于点多边少的稀疏图来说,邻接表效率大大提高。
广度优先遍历(BFS)
1. 原理
图的广度优先遍历类似于树的层序遍历。
2. 代码
邻接矩阵
//邻接矩阵的广度优先遍历
void DFSTraverse(MGraph G)
{
int i, j;
Queue Q;
for(i = 0; i < G.numVertexes; i++)
{
visited[i] = FALSE;
}
InitQueue(&Q); //初始化队列
for(i = 0 ; i < G.numVertexes; i++)
{
if(!visited[i]) //未访问的就处理(连通图只执行一次)
{
visited[i] = TRUE;
printf("%c", G.vexs[i]);
EnQueue(&Q, i); //将此顶点加入队列
while(!QueueEmtpy(Q))
{
DeQueue(&Q, &i); //将队中元素出队列, 赋值给i
for(j = 0; j < G.numVertexes; j++)
{
if(G.arc[i][j] == 1 && !visited[j]) //判断其他顶点若与当前顶点存在边且未被访问过
{
visited[j] = TRUE;
printf("%c", G.vexs[j]);
EnQueue(&Q, j);
}
}
}
}
}
} //对队列元素的操作是在入队时进行
邻接表
//邻接表的广度优先遍历
void BFSTraverse(GraphAdjList GL)
{
int i;
EdgeNode *p;
Queue Q;
for(i = 0; i < GL.numVertexes; i++)
{
visited[i] = FALSE;
}
InitQueue(&Q);
for(i = 0; i < GL.numVertexes; i++)
{
if(!visited[i])
{
visited[i] = TRUE;
printf("%c", GL.adjList[i].data);
EnQueue(&Q, i);
while(!QueueEmpty(Q))
{
DeQueue(&Q, &i);
p = GL.adjList[i].firstedge;
while(p)
{
if(!visited[p -> adjvex])
{
visited[p -> adjvex] = TRUE;
printf("%c ", GL.adjList[p -> adjvex].data);
EnQueue(&Q, p -> adjvex); //入队列
}
p = p -> next;
}
}
}
}
}
总结
深度优先更适合目标比较明确的情况,广度优先更适合在不断扩大遍历范围时找到相对最优解的情况。