1、深度优先遍历
*******、遍历过程(和树的先序遍历思想一样)
深度优先搜素(DFS)遍历类似于树的先序遍历,是树的先序遍历的推广!
(1)、从图中某个顶点v出发,访问v
(2)、找出刚访问过顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,重复此步骤,直到刚访问过的顶点没有未被访问的邻接点为止!
(3)、返回前一个访问过的且仍有未被访问的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点!(沿着原路返回执行(3))
(4)、重复(2)(3)步骤,直到所有的顶点都被访问过,搜索停止!
*******、算法步骤
(1)、从某个顶点v出发,访问v,并修改访问信息为true
(2)、依次检查v的所有邻接点w,如果visited[v] 的值为false,再从w出发进行递归遍历,直到图中所有的顶点都被访问!
*******、算法实现
//深度优先遍历非连通图
void DFSTraverse(Graph G){
//对非连通图G做深度优先遍历
for(int v = 0; v < G.vexnum: ++V){
visited[v] = false; //访问数组初始化
}
for(int v = 0; v < G.vexnum: ++V){ //循环调用算法DFS
if (!visited[v]) DFS(G,v); //对尚未访问的顶点调用DFS算法
}
}
void DFS(Graph G,int v){ //从第v个顶点出发递归第深度优先遍历图G
cout<<v; //打印当前访问的点
visited[v] = true; //修改访问变量
for(int w = FisrstAdjvex(G,V); w >= 0; w = NextAdjvex(G,v,w){
//依次检查v的所有邻接点,FisrstAdjvex(G,V)表示的是v的第一个邻接点
//NextAdjvex(G,v,w)表示v相对于w的下一个邻接点,w >= 0表示存在邻接点
if (!visited[w]) DFS(G,w); //对尚未访问的顶点调用DFS算法
}
}
*******、算法分析
(1)、首先深度优先遍历结果是不唯一的,而且是使用栈的思想解决问题的!实际是使用递归来调用系统提供的栈来解决问题的!
(2)、分析上述算法,在遍历图的时候,对图中的每个顶点至多调用依次DFS函数,因为一旦某个顶点被已经访问过,就不再从他出发进行搜索。因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程,所消耗的时间取决于算法所采用的存储结构!当采用邻接矩阵表示图的时候,查找其每个顶点的邻接点的时间复杂度为o(n*n),其中n是图中的顶点数。而当以邻接表做图的存储结构的时候,查找邻接点的时间复杂度为o(e),其中e为图的边数。因此,当采用邻接表做存储结构的时候,深度优先遍历搜索的时间复杂度是o(n+e)(n个顶点存储在数组中)
2、广度优先遍历
*******、遍历过程(和树的层次遍历思想是一样的)
广度优先遍历算法类似于树的层次遍历算法!
(1)、从图中某个顶点出发,访问v
(2)、依次访问v的各个未曾访问到的邻接点
(3)、分别从这些邻接点出发依次访问他们的邻接点,并且使“先被访问的顶点的邻接点”先于“后被访问的顶点的邻接点”被访问。重复步骤(3),直到图中所有已被访问的顶点的邻接点都被访问到!
*******、算法步骤
(1)、从某个顶点v出发,访问v,并修改访问信息为true,然后将v进队
(2)、只要队列不空,则重复执行以下操作:
*******、队头顶点u出队
*******、依次检查v的所有邻接点w,如果visited[v] 的值为false,则访问w,并置访问信息为true,然后将w进队!
*******、算法实现
//广度优先遍历非连通图
void BFSTraverse(Graph G){
//对非连通图G做深度优先遍历
for(int v = 0; v < G.vexnum: ++V){
visited[v] = false; //访问数组初始化
}
for(int v = 0; v < G.vexnum: ++V){ //循环调用算法BFS
if (!visited[v]) BFS(G,v); //对尚未访问的顶点调用BFS算法
}
}
void BFS(Graph G,int v){ //从第v个顶点出发广度优先遍历图G
cout<<v; //打印当前访问的点
visited[v] = true; //修改访问变量
InitQueue(Q); //初始化辅助存储空间
Enqueue(Q,v); //v进队
while(!QueueEmpty(Q){ //队列非空
Dequeue(Q,u); //队头元素出队并置为u
for(int w = FisrstAdjvex(G,u); w >= 0; w = NextAdjvex(G,u,w){ //找队头元素的各个邻接点
//依次检查u的所有邻接点,FisrstAdjvex(G,u)表示的是v的第一个邻接点
//NextAdjvex(G,u,w)表示u相对于w的下一个邻接点,w >= 0表示存在邻接点
if (!visited[w])
{
cout<<w;
visited[w] = true; //访问w,并修改访问标志
Enqueue(Q,w); //w进队
}
}
}
}
*******、算法分析
广度优先遍历可以看成是树的层次遍历,使用队列的思想!分析上面的算法,每个顶点最多进一次队列。遍历图的实质上是通过边找邻接点的过程,因此广度优先搜索遍历图的时间复杂度和深度优先搜索遍历相同!所以使用邻接矩阵存储的时候,时间复杂度为o(n*n),使用邻接表实现存储结构的时候,时间复杂度为o(n+e)。两个唯一不同的是,两种方法使用的实现方法不同,一个是使用递归(背后调用的是系统栈),一个是使用队列,故两种遍历方法的不同之处仅仅在于对顶点访问的顺序不同而已!