[Cracking the Coding Interview] 4.1 Route Between Nodes 节点间的路径
Given a directed graph, design an algorithm to find out whether there is a route between nodes.
这道题让我们判断给定的一个图的任意两个节点间是否有路径。首先我们要知道如何定义一个图, 一般最常用的两种表示图的方法是: Adjacency Matrix 和 Adjacency List。
对选择用哪种方法来表示图取决于你要做什么样的操作和使用的难易程度。下面稍微总结了一下这两种方法的优缺点:
Adjacency Matrix
优点:邻接矩阵容易表示并且是用的过程可以清楚的看到节点之间的关系。删除一条边需要O(1)时间复杂度,判断两个节点间是否有路径非常快,只需要O(1)时间复杂度。
缺点:空间复杂度比较大O(V^2). 即使是稀疏矩阵也需要同样的空间。如果增加一个节点需要O(V^2)的时间复杂度。
Adjacency List
1 class Node 2 attr_accessor :name, :neighbors 3 4 def initialize(name) 5 @name = name 6 @neighbors = [] 7 end 8 end 9 10 class Graph 11 attr_accessor :nodes 12 13 def initialize 14 @nodes = [] 15 end 16 end
优点:节省空间O(V+E). 增加一个节点很容易O(1)。
缺点:判断两个节点是否右边不是很快,需要O(V)的时间复杂度。
所以这道题在使用不同的图的表示方法的时候,判断两点间的路径方法是不一样的。我们假设使用Adjacency List的方法来表示图。
如何判断节点间是否有路径呢?这道题很简单,我们只需要使用DFS或者BFS来遍历整个图就可以了。
参见如下代码:
BFS Version
class Node attr_accessor :name, :neighbors, :visited def initialize(name) @name = name @neighbors = [] @visited = false end end def has_route?(node1, node2) return false if node1.nil? || node2.nil? q = Queue.new node1.visited = true q << node1 while !q.empty? cur = q.pop return true if cur.name == node2.name cur.neighbors.each do |n| if !n.visited n.visited = true q << n end end end false end
DFS Version
class Node attr_accessor :name, :neighbors, :visited def initialize(name) @name = name @neighbors = [] @visited = false end end def has_route?(node1, node2) return false if node1.nil? || node2.nil? return true if node1.name == node2.name node1.visited = true node1.neighbors.each do |n| next if n.visited if has_route?(n, node2) return true end end false end