require 'rgl/base' require 'rgl/traversal' module RGL module Graph # Separates graph's vertices into two disjoint sets so that every edge of the graph connects vertices from different # sets. If it's possible, the graph is bipartite. # # Returns an array of two disjoint vertices sets (represented as arrays) if the graph is bipartite. Otherwise, # returns nil. # @return [Array] def bipartite_sets raise NotUndirectedError.new('bipartite sets can only be found for an undirected graph') if directed? bfs = BipartiteBFSIterator.new(self) # if necessary, we start BFS from each vertex to make sure # that all connected components of the graph are processed each_vertex do |u| next if bfs.finished_vertex?(u) bfs.reset_start(u) bfs.move_forward_until { bfs.found_odd_cycle } return if bfs.found_odd_cycle end bfs.bipartite_sets_map.inject([[], []]) do |sets, (vertex, set)| sets[set] << vertex sets end end # Returns true if the graph is bipartite. Otherwise returns false. # def bipartite? !bipartite_sets.nil? end end # module Graph class BipartiteBFSIterator < BFSIterator attr_reader :bipartite_sets_map, :found_odd_cycle def reset super @bipartite_sets_map = {} @found_odd_cycle = false end def set_to_begin super @bipartite_sets_map[@start_vertex] = 0 end def reset_start(new_start) @start_vertex = new_start set_to_begin end def handle_tree_edge(u, v) @bipartite_sets_map[v] = (@bipartite_sets_map[u] + 1) % 2 unless u.nil? # put v into the other set end def handle_back_edge(u, v) verify_odd_cycle(u, v) end def handle_forward_edge(u, v) verify_odd_cycle(u, v) end private def verify_odd_cycle(u, v) u_set = @bipartite_sets_map[u] @found_odd_cycle = true if u_set && u_set == @bipartite_sets_map[v] end end # class BipartiteBFSIterator end # module RGL