深度优先搜索(Depth-first search)是如何搜索一张图的?

思想:对于最新发现的顶点v,若是它还有以此为起点而还未探索的边,沿此边探索。若是v的全部边已经探索完了,再回溯到发现v有起始点的那些边。一直到已经探索了从源起点可到的全部顶点为止。若是还有没探索的顶点,将它定义为一个新的源顶点,继续上述过程。算法

像走迷宫同样,尝试每种可能的结果,没走通,就回溯到当初分叉的路口,继续探索bash

探索整个的图

DFS(V,Adj):
    parent={}
    for s in V: //遍历图中全部的点
        if s not in parent: //点没有遍历过就继续探索
            parent[s]=None //源点不设置父节点
            DFS-Visit(Adj,s) //探索当前源顶点可以到达的全部的点
复制代码

给定一个源顶点s,DFS的实现方式

DFS-Visit(Adj,s):
   for v in Adj[s]: //遍历全部的边
       if v not in parent: //当前边没有遍历过
           parent[v]=s //记录已经遍历
           DFS-Visit(adj,v) //优先探索当前节点的边,完成以后,再执行回溯(经过循环实现)
复制代码

以有向图为例
假设按照字母的顺序来遍历全部的顶点,即V=[a,b,c,d,e,f],原始的图为spa

  1. 第一步探索a到b的边,发现b还有边,一直往下走,直到d为止,d没有往下走的边,第一个DFS-Visit执行结束

2. 开始回溯,先回溯到d的父节点e,一样没有,一直到a,a的另外一条边是a到d,可是d已经探索过,再也不操做

3. 换源点执行探索,此时为b,可是b已经探索过,再探索c发现仅一条边对应的f没探索过

  1. 继续更换源点一直到f,都没有新的还没有探索过的边,最终DFS探索生成了两颗深度优先树

深度优先树指的是通过DFS生成的树,结果为3中的橙色箭头所指的两个部分.net

时间复杂度

O(V+E);它的遍历规则仍然须要遍历全部的节点一遍,对于每条变来说,只有没有遍历过的才作一次遍历3d

深度优先搜索的用途是什么?

1. 边分类

  • 树边。若是顶点v是在探寻边(u,v)首次发现的,那么(u,v)是树边,好比图中(a,b)
  • 正向边。u到v的某个非树边,好比图中(a,d),a到d存在边,可是没有进入树中
  • 反边。 链接u到它的祖先顶点v的边,好比图中的(d,b)
  • 交叉边。生成的树中,两个顶点不存在父子关系。好比图中的 (g,d)

在算法中,树边判断经过parent就能够看出,parent的逆向就是树边,得以标识;code

反边判断,能够在源点开始作一个标记,把全部的通过的节点都放入栈中,若是下次获得的顶点在栈中,那么这条边就是反边;cdn

如何判断在一个图中是否存在环?

图G存在环,当且仅当图中存在一条反边。证实以下:blog

  • 存在环,证实有反边。假设从环中选取一个点v_0,做为DFS遍历的第一个顶点,证实 (v_k,v_0)是反边:已知事实是,v_1访问前,v_0一定已经访问完,v_k访问前,v_0一定已经访问完,当访问到v_k再下一个查看v_0的时候,v_0已经在栈中,那么(v_k,v_0)是反边

  • 存在反边,证实有环。首先s到t必然存在树边,而后t到s是一条反边,那必然成环

2. Job调度

Job自己是个无环的有向图,各个顶点之间一定存在着必定的顺序,执行的时候等前面的执行完才能再执行排在后面的排序

它使用的算法称做拓扑排序,拓扑排序内部使用的就是DFS,输出为完成时的顶点的逆序,就排序好了(以前记录的是parent的指向,真正的执行是先parent)it

这种排序产生的结果是,假设图中全部的顶点放在同一个水平线上,那么全部的方向均是从左到右

证实

只须要证实,对于任何一个边e=(u,v),在v执行完以后,u才执行完

  • u的访问发生在v以前:因为u,v之间存在一条边,那么在u执行完以前,确定为访问v,而等v完成后,才能完成u
  • v的访问在u以前:因为u和v之间存在一条边,若是先访问了v,若是存在v到u的路径,这样会有环,因此根本不会执行u,也就是说,v先执行完

附录

算法导论(MIT 6.006 第14讲)

相关文章
相关标签/搜索