zoukankan      html  css  js  c++  java
  • 深度优先搜索

    深度优先搜索(DFS:Depth-First Search)是一种图搜索策略,其将搜索限制到 2 种操作:

    • (a) 访问图中的一个节点;
    • (b) 访问该节点的子节点;

    在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续探测下去。当顶点 v 的所有边都已被探寻过后,搜索将回溯到发现顶点 v 有起始点的那些边。这一过程一直进行到已发现从源顶点可达的所有顶点为止。实际上深度优先搜索最初的探究也是为了解决迷宫问题。

    对图的深度优先搜索与对树(Tree)的深度优先遍历(Depth First Traversal)是类似的,区别在于图中可能存在环,所以可能会遍历到已经遍历的节点。

    例如,下面的图中,从顶点 2 开始遍历,当遍历到顶点 0 时,子顶点为 1 和 2,而顶点 2 已经遍历过,如果不做标记,遍历过程将陷入死循环。所以,在 DFS 的算法实现中需要对顶点是否访问过做标记。

    上图的 DFS 遍历结果为 2, 0, 1, 3。

    DFS 算法可以通过不同方式来实现:

    • 递归方式
    • 非递归方式:使用栈(Stack)数据结构来存储遍历图中节点的中间状态;

    DFS 算法的递归方式伪码如下:

    1 procedure DFS(G,v):
    2     label v as discovered
    3     for all edges from v to w in G.adjacentEdges(v) do
    4         if vertex w is not labeled as discovered then
    5             recursively call DFS(G,w)

    DFS 算法的非递归方式伪码如下:

    1 procedure DFS-iterative(G,v):
    2     let S be a stack
    3     S.push(v)
    4     while S is not empty
    5           v ← S.pop() 
    6           if v is not labeled as discovered:
    7               label v as discovered
    8               for all edges from v to w in G.adjacentEdges(v) do
    9                   S.push(w)

    深度优先搜索(DFS)的时间复杂度为 O(V+E),V 即 Vertex 顶点数量,E 即 Edge 边数量。

    DFS 算法实现代码如下:

      1 using System;
      2 using System.Linq;
      3 using System.Collections.Generic;
      4 
      5 namespace GraphAlgorithmTesting
      6 {
      7   class Program
      8   {
      9     static void Main(string[] args)
     10     {
     11       Graph g = new Graph(4);
     12       g.AddEdge(0, 1);
     13       g.AddEdge(0, 2);
     14       g.AddEdge(1, 2);
     15       g.AddEdge(2, 0);
     16       g.AddEdge(2, 3);
     17       g.AddEdge(3, 3);
     18 
     19       foreach (var vertex in g.DFS(2))
     20       {
     21         Console.WriteLine(vertex);
     22       }
     23       foreach (var vertex in g.RecursiveDFS(2))
     24       {
     25         Console.WriteLine(vertex);
     26       }
     27 
     28       Console.ReadKey();
     29     }
     30 
     31     class Edge
     32     {
     33       public Edge(int begin, int end)
     34       {
     35         this.Begin = begin;
     36         this.End = end;
     37       }
     38 
     39       public int Begin { get; private set; }
     40       public int End { get; private set; }
     41     }
     42 
     43     class Graph
     44     {
     45       private Dictionary<int, List<Edge>> _adjacentEdges
     46         = new Dictionary<int, List<Edge>>();
     47 
     48       public Graph(int vertexCount)
     49       {
     50         this.VertexCount = vertexCount;
     51       }
     52 
     53       public int VertexCount { get; private set; }
     54 
     55       public void AddEdge(int begin, int end)
     56       {
     57         if (!_adjacentEdges.ContainsKey(begin))
     58         {
     59           var edges = new List<Edge>();
     60           _adjacentEdges.Add(begin, edges);
     61         }
     62 
     63         _adjacentEdges[begin].Add(new Edge(begin, end));
     64       }
     65 
     66       public List<int> DFS(int start)
     67       {
     68         List<int> traversal = new List<int>();
     69         int current = start;
     70 
     71         // mark all the vertices as not visited
     72         bool[] visited = new bool[VertexCount];
     73         for (int i = 0; i < VertexCount; i++)
     74         {
     75           visited[i] = false;
     76         }
     77 
     78         // create a stack for DFS
     79         Stack<int> stack = new Stack<int>();
     80 
     81         // mark the current node as visited and push it
     82         visited[current] = true;
     83         stack.Push(current);
     84 
     85         while (stack.Count > 0)
     86         {
     87           current = stack.Pop();
     88 
     89           // if this is what we are looking for
     90           traversal.Add(current);
     91 
     92           // get all child vertices of the popped vertex,
     93           // if a child has not been visited, 
     94           // then mark it visited and push it
     95           if (_adjacentEdges.ContainsKey(current))
     96           {
     97             foreach (var edge in _adjacentEdges[current].OrderByDescending(e => e.End))
     98             {
     99               if (!visited[edge.End])
    100               {
    101                 visited[edge.End] = true;
    102                 stack.Push(edge.End);
    103               }
    104             }
    105           }
    106         }
    107 
    108         return traversal;
    109       }
    110 
    111       public List<int> RecursiveDFS(int start)
    112       {
    113         List<int> traversal = new List<int>();
    114         int current = start;
    115 
    116         // mark all the vertices as not visited
    117         bool[] visited = new bool[VertexCount];
    118         for (int i = 0; i < VertexCount; i++)
    119         {
    120           visited[i] = false;
    121         }
    122 
    123         // traversal
    124         RecursiveDFSTraversal(current, visited, traversal);
    125 
    126         return traversal;
    127       }
    128 
    129       private void RecursiveDFSTraversal(int current, bool[] visited, List<int> traversal)
    130       {
    131         visited[current] = true;
    132         traversal.Add(current);
    133 
    134         if (_adjacentEdges.ContainsKey(current))
    135         {
    136           foreach (var edge in _adjacentEdges[current].OrderBy(e => e.End))
    137           {
    138             if (!visited[edge.End])
    139             {
    140               RecursiveDFSTraversal(edge.End, visited, traversal);
    141             }
    142           }
    143         }
    144       }
    145     }
    146   }
    147 }

    参考资料

    本篇文章《深度优先搜索》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

  • 相关阅读:
    java面试准备之基础排序——冒泡与选择排序
    PL/SQL 存储过程
    浅析Java中CountDownLatch用法
    tmux分屏幕
    two's complement
    angularJs中$controller的使用
    nodejs pipe实现大文件拷贝
    不错的网站
    echarts文档对照
    nodejs 项目的session验证
  • 原文地址:https://www.cnblogs.com/gaochundong/p/depth_first_search.html
Copyright © 2011-2022 走看看