先来看图的深度优先遍历:
深度优先遍历的基本思想是访问顶点V0,然后访问V0邻接到的未被访问的顶点V1,再从V1出发递归地按照深度优先的方式遍历。当遇到一个所有邻接于它的顶点都被访问过了的顶点u时,则回到自己访问顶点序列中最后一个拥有未被访问相邻顶点的顶点w,从w出发继续访问。最终当任何已被访问过的顶点都没有未被访问的相邻顶点时,遍历结束。也就是说,深度优先遍历是沿着图的某一条分支遍历,直到末端,然后回溯,沿着另一支分支进行同样的遍历,直到所有的分支都被遍历过为止。
如下图所示,从v1开始遍历,先访问v2,再继续访问得v6,v5,v4,此时不可继续进行访问,回溯至v2继续访问v7,v3,此时发现所有点的相邻节点都已被访问,则访问结束,最终得到的深度优先遍历序列为:v1,v2,v6,v5,v4,v7,v3.
深度优先遍历实现代码如下(这里用链式前向星存储图):
bool s[maxn]={0};
void DFS(int x)
{
s[x]=true;//标记当前点已被访问
cout<<x<<endl;
for(int i=head[x],i!=-1;i=edge[i].next)
if(!s[edge[i].to)
DFS(edge[i].to);
}
深度优先遍历的实质是检查每个顶点的邻接顶点是否已经被访问过的过程,即对每条边处理一次,对每个顶点有效访问一次,所以如果存储结构用邻接矩阵,对于每个顶点查找其邻接点的效率是O(n),进而遍历效率是O(n^2)。我们采用前向星或链式前向星的存储结构的话,每条边都被处理一次,则遍历效率是O(m)。
图的宽度优先遍历
图的宽度优先遍历与深度优先遍历的不同之处在于:宽度优先遍历访问顶点v0,然后访问v0邻接到的所有未被访问的顶点v1,v2,...,vi,再以此访问v1,v2,...,vi邻接到的未被访问的所有顶点,如此进行下去,直到所有的顶点都被访问到。
对于上图来说,从v1开始访问,先访问v1所有的相邻顶点v2,v3,v4,v5,然后顺序访问这4个节点的相邻节点,即v2的相邻节点v6,v7。此时由于v3,v4,v5没有相邻的未被访问节点,那么该访问v6,v7的相邻节点,发现都已被访问过,遍历结束。最终得到的宽度优先遍历序列为:v1,v2,v3,v4,v5,v6,v7.
实现代码如下:
bool s[maxn];
void BFS(int x)
{
int queue[maxn];
int tmp=0;
queue[tmp++]=x;
for(int i=0;i<tmp;i++)
{
//每次从队头取节点,一定是下一个待扩展节点
cout<<queue[i]<<endl;
for(int k=head[queue[i]];k!=-1;k=edge[k].next)
if(!s[edge[k].to)
queue[tmp++]=edge[k].to;
}
}
宽度优先搜索的实质与深度优先搜索相同,都是通过边来检查邻接的顶点是否已被访问过的过程,因此时间复杂度相同,只是遍历得到的访问次序不同。
广搜与深搜的小区别
一般来说,广搜常用于找单一的最短路线,或者是规模小的路径搜索,它的特点是"搜到就是最优解", 而深搜用于找多个解或者是"步数已知(好比3步就必需达到前提)"的标题,它的空间效率高,然则找到的不必定是最优解,必需记实并完成全数搜索,故一般情况下,深搜需要很是高效的剪枝(优化).
像搜索最短路径这些的很显著若是用广搜,因为广搜的特征就是一层一层往下搜的,保证当前搜到的都是最优解,当然,最短路径只是一方面的操作,像什么起码状态转换也是可以操作的。
深搜就是优先搜索一棵子树,然后是另一棵,它和广搜对比,有着内存需要相对较少的所长,八皇后标题就是典范楷模的操作,这类标题很显著是不能用广搜往解决的。或者像图论里面的找圈的算法,数的前序中序后序遍历等,都是深搜
深搜和广搜的分歧之处是在于搜索次序的分歧。
深搜的实现近似于栈,每次选择栈顶元素往扩年夜,
广搜则是操作了队列,先被扩年夜的的节点优先拿往扩年夜。
搜索树的形态:深搜层数良多,广搜则是很宽。
深搜合适找出所有方案,广搜则用来找出最佳方案
深搜和广搜的分歧:
深搜并不能保证第一次碰着方针点就是最短路径,是以要搜索所有可能的路径,是以要回溯,标识表记标帜做了之后还要打消失踪,是以统一个点可能被访谒良多良多次。而广搜因为它的由近及远的结点扩年夜次序,结点老是以最短路径被访谒。一个结点假如第二次被访谒,第二次的路径确定不会比第一次的短,是以就没有需要再从这个结点向周围扩年夜――第一次访谒这个结点的时辰已经扩年夜过了,第二次再扩年夜只会获得更差的解。是以做过的标识表记标帜不必往失踪。是以统一个点至多只可能被访谒一次。每访谒一个结点,与它相连的边就被搜检一次。是以最坏情况下,所有边都被搜检一次,是以时刻复杂度为O(E)。