Route Between Two Nodes in Graph

Question

Problem Statement

Given a directed graph, design an algorithm to find out whether there is a
route between two nodes.

Example

Given graph:

  1. A----->B----->C
  2. \ |
  3. \ |
  4. \ |
  5. \ v
  6. ->D----->E

for s = B and t = E, return true

for s = D and t = C, return false

题解1 - DFS

检测图中两点是否通路,图搜索的简单问题,DFS 或者 BFS 均可,注意检查是否有环即可。这里使用哈希表记录节点是否被处理较为方便。深搜时以起点出发,递归处理其邻居节点,需要注意的是处理邻居节点的循环时不是直接 return, 而只在找到路径为真时才返回 true, 否则会过早返回 false 而忽略后续可能满足条件的路径。

Java

  1. /**
  2. * Definition for Directed graph.
  3. * class DirectedGraphNode {
  4. * int label;
  5. * ArrayList<DirectedGraphNode> neighbors;
  6. * DirectedGraphNode(int x) {
  7. * label = x;
  8. * neighbors = new ArrayList<DirectedGraphNode>();
  9. * }
  10. * }
  11. */
  12. public class Solution {
  13. /**
  14. * @param graph: A list of Directed graph node
  15. * @param s: the starting Directed graph node
  16. * @param t: the terminal Directed graph node
  17. * @return: a boolean value
  18. */
  19. public boolean hasRoute(ArrayList<DirectedGraphNode> graph,
  20. DirectedGraphNode s, DirectedGraphNode t) {
  21. Set<DirectedGraphNode> visited = new HashSet<DirectedGraphNode>();
  22. return dfs(graph, s, t, visited);
  23. }
  24. public boolean dfs(ArrayList<DirectedGraphNode> graph,
  25. DirectedGraphNode s, DirectedGraphNode t,
  26. Set<DirectedGraphNode> visited) {
  27. if (s == t) {
  28. return true;
  29. } else {
  30. // corner cases
  31. if (s == null || t == null) return false;
  32. // flag visited node, avoid cylic
  33. visited.add(s);
  34. // compare unvisited neighbor nodes recursively
  35. if (s.neighbors.size() > 0) {
  36. for (DirectedGraphNode node : s.neighbors) {
  37. if (visited.contains(node)) continue;
  38. if (dfs(graph, node, t, visited)) return true;
  39. }
  40. }
  41. }
  42. return false;
  43. }
  44. }

源码分析

根据构造函数的实现,Java 中判断是否有邻居节点时使用.size,而不是null. 注意深搜前检测是否被处理过。行

  1. if (dfs(graph, node, t, visited)) return true;

中注意不是直接 return, 只在为 true 时返回。

复杂度分析

遍历所有点及边,时间复杂度为 O(V+E).

题解2 - BFS

除了深搜处理邻居节点,我们也可以采用 BFS 结合队列处理,优点是不会爆栈,缺点是空间复杂度稍高和实现复杂点。

Java

  1. /**
  2. * Definition for Directed graph.
  3. * class DirectedGraphNode {
  4. * int label;
  5. * ArrayList<DirectedGraphNode> neighbors;
  6. * DirectedGraphNode(int x) {
  7. * label = x;
  8. * neighbors = new ArrayList<DirectedGraphNode>();
  9. * }
  10. * }
  11. */
  12. public class Solution {
  13. /**
  14. * @param graph: A list of Directed graph node
  15. * @param s: the starting Directed graph node
  16. * @param t: the terminal Directed graph node
  17. * @return: a boolean value
  18. */
  19. public boolean hasRoute(ArrayList<DirectedGraphNode> graph,
  20. DirectedGraphNode s, DirectedGraphNode t) {
  21. if (graph == null || s == null || t == null) return false;
  22. Queue<DirectedGraphNode> q = new LinkedList<DirectedGraphNode>();
  23. Set<DirectedGraphNode> visited = new HashSet<DirectedGraphNode>();
  24. q.offer(s);
  25. while (!q.isEmpty()) {
  26. int qLen = q.size();
  27. for (int i = 0; i < qLen; i++) {
  28. DirectedGraphNode node = q.poll();
  29. visited.add(node);
  30. if (node == t) return true;
  31. // push neighbors into queue
  32. if (node.neighbors.size() > 0) {
  33. for (DirectedGraphNode n : node.neighbors) {
  34. // avoid cylic
  35. if (visited.contains(n)) continue;
  36. q.offer(n);
  37. }
  38. }
  39. }
  40. }
  41. return false;
  42. }
  43. }

源码分析

同题解一。

复杂度分析

时间复杂度同题解一,也是 O(V+E), 空间复杂度最坏情况下为两层多叉树,为 O(V+E).