Graph Traversal BFS + DFS

Graph 的例子包括

  • City Map
  • Power Distribution Network
  • Water Distribution Network
  • Flight Network or Airports

相关概念:

  • Vertex / Vertices
  • Edge
  • Degree: the degree (or valency) of a vertex of a graph is the number of edges incident to the vertex, with loops counted twice
  • Directed / Undirected
  • Simple Graph: graphs with no parallel edges or self-loops
  • Connected: for any two vertices, there is a path between them
  • Strongly Connected: A directed graph is strongly connected if for any two vertices u and v of the graph , u reaches v and v reaches u
  • Forest: A forest is an undirected graph, all of whose connected components are trees; in other words, the graph consists of a disjoint union of trees. Equivalently, a forest is an undirected acyclic graph. As special cases, an empty graph, a single tree, and the discrete graph on a set of vertices (that is, the graph with these vertices that has no edges), are examples of forests.

Graph ADT:

  • field variables:
    • Collection of vertices
    • Collection of edges
  • numOfVertices()
  • numOfEdges()
  • getVertices(): return collection of vertices
  • getEdges(): return collection of edges
  • outDegree(Vertex v) / inDegree(Vertex v)
  • add(Vertex v)
  • add(Edge e, Vertex v, Vertex u)
  • removeVertex(Vertex v)
  • removeEdge(Edge e)

Graph 的若干总表达方式:

  • Edge List
  • Adjacency List:
    • getEdge(Vertex v, Vertex u): O( min ( deg(v), deg(u) ) )
  • Adjacency Map
  • Adjacency Matrix


public class GraphNode {
    int val;
    List<GraphNode> neighbors;

    public GraphNode(int val) {
        this.val = val;
        this.neighbors = new ArrayList<GraphNode>();
    }
}

DFS (O(2^n), O(n!)) (思想:构建搜索树+判断可行性)

  1. Find all possible solutions
  2. Permutations / Subsets

BFS (O(m), O(n))

  1. Graph traversal (每个点只遍历一次)
  2. Find shortest path in a simple graph

Graph BFS

HashSet<GraphNode> visited is for:

  1. Avoid infinite loop in Graph circle
  2. 消除冗余计算
public List<List<Integer>> bfsGraph(GraphNode root) {
    List<List<Integer>> res = new ArrayList<>();

    if (root == null) {
        return res;
    }

    Queue<GraphNode> queue = new LinkedList<>();
    Set<GraphNode> visited = new HashSet<>();
    queue.offer(root);
    visited.add(root);
    // int level = 0;

    while (!queue.isEmpty()) {
        // level++;
        ArrayList<Integer> layer = new ArrayList<>(queue.size());
        int size = queue.size();
        while (size > 0) {
            GraphNode node = queue.poll();
            layer.add(node.val);
            for (GraphNode neighbor: node.neighbors) {
                if (!visited.contains(neighbor)) {
                    queue.offer(neighbor);
                    visited.add(neighbor);
                }
            }
            size--;
        }
        res.add(layer);
    }

    return res;
}

comparing Binary Tree BFS traversal

public List<List<Integer>> bfsTree(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();

    if (root == null) {
        return res;
    }

    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        ArrayList<Integer> layer = new ArrayList<Integer>(queue.size());
        int size = queue.size();
        
        while(size > 0) {
            TreeNode node = queue.poll();
            layer.add(node.val);

            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }

            size--;
        }
        res.add(layer);
    }

    return res;
}

BFS 易于计算 minimum level for each node in Graph or Tree.

BFS 并不适于计算 maximum level for each GraphNode. 可能需要 max level 运算的题目: Topological Sorting. 此题题解:GeeksForGeeks, 尚未细看。

Graph BFS 也不适用于判断 Graph 中是否存在 circle,因为 visited Hash 已经将 circle 过滤处理了。DFS 应该更适用于判断 circle。



Graph DFS

使用 DFS 判断 Graph 中是否存在 circle:

public boolean hasCircle(GraphNode root) {
    if (root == null) {
        false;
    }

    Set<GraphNode> visited = new HashSet<>();

    return dfsGraph(root, visited);
}

private boolean dfsGraph(GraphNode root, Set<GraphNode> visited) {
    visited.add(root);

    for (GraphNode neighbor: root.neighbors) {
        if (visited.contains(neighbor)) {
            return true;
        }
        dfsGraph(neighbor, visited);
    }

    visited.remove(root);
    return false;
}

上面的实现方式存在冗余计算。这里的visited Hash实际上是 path. 优化的实现方法:

public boolean hasCircle(GraphNode root) {
   if (root == null) {
       false;
   }

   Set<GraphNode> path = new HashSet<>();
   Set<GraphNode> visited = new HashSet<>();

   return dfsGraph(root, path, visited);
}

private boolean dfsGraph(GraphNode root, Set<GraphNode> path, Set<GraphNode> visited) {
   visited.add(root);
   path.add(root);

   for (GraphNode neighbor: root.neighbors) {
       if (path.contains(neighbor)) {
           return true;
       }
       if (!visited.contains(neighbor)) {
           dfsGraph(neighbor, path);
       }
   }

   path.remove(root);
   return false;
}

题解中的 path: 1. add current node; 2. dfs neighbors; 3. remove current nodeBackTracking 的经典实现方式。实际上 BackTracking 也正是使用 DFS 来实现的。

以上基本就是 Graph DFS 的实现方式,不同于 Tree DFS 之处:

  • Graph DFS 只有 preorder 和 postorder 形式,inorder 并没有多大意义。
  • Graph DFS 同 Graph BFS一样,可以使用 visited Hash来消除冗余计算,防止 circle 死循环。
  • 如果涉及到 GraphNode depth,visited Hash是不可以使用的,因为需要遍历所有path,不同的path 对同一个 GraphNode depth 产生不一样的值。这种情况,可以使用 path Hash来防止死循环。

Problems Solved with Graph DFS

1) For an unweighted graph, DFS traversal of the graph produces the minimum spanning tree and all pair shortest path tree.

2) Detecting cycle in a graph
A graph has cycle if and only if we see a back edge during DFS. So we can run DFS for the graph and check for back edges.

3) Path Finding
We can specialize the DFS algorithm to find a path between two given vertices.

4) Topological Sorting
Topological Sorting is mainly used for scheduling jobs from the given dependencies among jobs. In computer science, applications of this type arise in instruction scheduling, ordering of formula cell evaluation when recomputing formula values in spreadsheets, logic synthesis, determining the order of compilation tasks to perform in makefiles, data serialization, and resolving symbol dependencies in linkers.

5) To test if a graph is bipartite
We can augment either BFS or DFS when we first discover a new vertex, color it opposited its parents, and for each other edge, check it doesn’t link two vertices of the same color. The first vertex in any connected component can be red or black! See this for details.

6) Finding Strongly Connected Components of a graph A directed graph is called strongly connected if there is a path from each vertex in the graph to every other vertex. (See this for DFS based algo for finding Strongly Connected Components)

7) Solving puzzles with only one solution, such as mazes. (DFS can be adapted to find all solutions to a maze by only including nodes on the current path in the visited set.)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 背景 一年多以前我在知乎上答了有关LeetCode的问题, 分享了一些自己做题目的经验。 张土汪:刷leetcod...
    土汪阅读 12,790评论 0 33
  • 课程介绍 先修课:概率统计,程序设计实习,集合论与图论 后续课:算法分析与设计,编译原理,操作系统,数据库概论,人...
    ShellyWhen阅读 2,385评论 0 3
  • 一、动态规划 找到两点间的最短路径,找最匹配一组点的线,等等,都可能会用动态规划来解决。 参考如何理解动态规划中,...
    小碧小琳阅读 25,269评论 2 20
  • 不为苦而悲,不受宠而欢。
    晚风吻尽你阅读 275评论 0 0
  • 昨晚听着音乐睡着了,今天怎么都没力气起来,平时七点多起床,今天一直睡到十点多,还头疼,浑身没力。心里也提不起劲。我...
    觉知中的帆阅读 114评论 0 0