mirror of
https://github.com/TheAlgorithms/Ruby
synced 2024-11-16 19:50:00 +01:00
Merge pull request #208 from aparibocci/feature/graph_topological_sort
Implementing topological sorting for DAGs
This commit is contained in:
commit
5be0a3db59
2 changed files with 89 additions and 0 deletions
37
data_structures/graphs/topological_sort.rb
Normal file
37
data_structures/graphs/topological_sort.rb
Normal file
|
@ -0,0 +1,37 @@
|
|||
require 'set'
|
||||
|
||||
##
|
||||
# This class aims to provide topological sorting capabilities for directed acyclic graphs.
|
||||
#
|
||||
# Topological sorting runs in O(|V|), where |V| is the number of graph nodes.
|
||||
|
||||
class TopologicalSorter
|
||||
attr_reader :graph
|
||||
|
||||
def initialize(graph)
|
||||
raise ArgumentError, "Topological sort is only applicable to directed graphs!" unless graph.directed
|
||||
@graph = graph
|
||||
end
|
||||
|
||||
def topological_sort
|
||||
@sorted_nodes = []
|
||||
@seen = Set[]
|
||||
@visited = Set[]
|
||||
for node in graph.nodes
|
||||
dfs_visit(node)
|
||||
end
|
||||
@sorted_nodes
|
||||
end
|
||||
|
||||
private
|
||||
def dfs_visit(node)
|
||||
return if @visited.include?(node)
|
||||
raise ArgumentError, "Cycle in graph detected on node #{node}!" if @seen.include?(node)
|
||||
@seen.add(node)
|
||||
for neighbor in graph.neighbors(node)
|
||||
dfs_visit(neighbor)
|
||||
end
|
||||
@visited.add(node)
|
||||
@sorted_nodes.unshift(node)
|
||||
end
|
||||
end
|
52
data_structures/graphs/topological_sort_test.rb
Normal file
52
data_structures/graphs/topological_sort_test.rb
Normal file
|
@ -0,0 +1,52 @@
|
|||
require 'minitest/autorun'
|
||||
require_relative 'topological_sort'
|
||||
require_relative 'unweighted_graph'
|
||||
|
||||
class TestTopologicalSort < Minitest::Test
|
||||
def test_topological_sort_returns_valid_order_for_acyclic_graph
|
||||
wardrobe_items = [:underwear, :trousers, :belt, :shirt, :tie, :jacket, :socks, :shoes, :watch]
|
||||
wardrobe_graph = UnweightedGraph.new(nodes: wardrobe_items, directed: true)
|
||||
wardrobe_graph.add_edge(:underwear, :trousers)
|
||||
wardrobe_graph.add_edge(:underwear, :shoes)
|
||||
wardrobe_graph.add_edge(:socks, :shoes)
|
||||
wardrobe_graph.add_edge(:trousers, :shoes)
|
||||
wardrobe_graph.add_edge(:trousers, :belt)
|
||||
wardrobe_graph.add_edge(:shirt, :belt)
|
||||
wardrobe_graph.add_edge(:belt, :jacket)
|
||||
wardrobe_graph.add_edge(:shirt, :tie)
|
||||
wardrobe_graph.add_edge(:tie, :jacket)
|
||||
|
||||
sorted_items = TopologicalSorter.new(wardrobe_graph).topological_sort
|
||||
|
||||
assert sorted_items.index(:underwear) < sorted_items.index(:trousers)
|
||||
assert sorted_items.index(:underwear) < sorted_items.index(:shoes)
|
||||
assert sorted_items.index(:socks) < sorted_items.index(:shoes)
|
||||
assert sorted_items.index(:trousers) < sorted_items.index(:shoes)
|
||||
assert sorted_items.index(:trousers) < sorted_items.index(:belt)
|
||||
assert sorted_items.index(:shirt) < sorted_items.index(:belt)
|
||||
assert sorted_items.index(:belt) < sorted_items.index(:jacket)
|
||||
assert sorted_items.index(:shirt) < sorted_items.index(:tie)
|
||||
assert sorted_items.index(:tie) < sorted_items.index(:jacket)
|
||||
end
|
||||
|
||||
def test_topological_sort_raises_exception_for_undirected_graph
|
||||
nodes = [:u, :v]
|
||||
graph = UnweightedGraph.new(nodes: nodes, directed: false)
|
||||
graph.add_edge(:u, :v)
|
||||
|
||||
assert_raises ArgumentError do
|
||||
TopologicalSorter.new(graph).topological_sort
|
||||
end
|
||||
end
|
||||
|
||||
def test_topological_sort_raises_exception_for_cyclic_graph
|
||||
nodes = [:u, :v]
|
||||
graph = UnweightedGraph.new(nodes: nodes, directed: true)
|
||||
graph.add_edge(:u, :v)
|
||||
graph.add_edge(:v, :u)
|
||||
|
||||
assert_raises ArgumentError do
|
||||
TopologicalSorter.new(graph).topological_sort
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue