├── README.md ├── bellman_ford ├── README.md ├── bellman_ford.rb ├── edge.rb ├── graph.rb ├── node.rb └── spec.rb ├── breadth_first_search ├── README.md ├── breadth_first_search.rb ├── graph.rb ├── node.rb └── spec.rb ├── connected_components ├── README.md ├── connected_components.rb ├── graph.rb ├── node.rb └── spec.rb ├── depth_first_search ├── README.md ├── depth_first_search.rb ├── graph.rb ├── node.rb └── spec.rb ├── dijkstra ├── README.md ├── dijkstra.rb ├── edge.rb ├── graph.rb ├── node.rb ├── priority_queue.rb └── test.rb ├── kosaraju_strong_components ├── README.md ├── depth_first_order.rb ├── edge.rb ├── graph.rb ├── node.rb ├── spec.rb └── strong_components.rb ├── kruskal ├── README.md ├── edge.rb ├── graph.rb ├── kruskal.rb ├── node.rb ├── test.rb └── union_find.rb └── topological_sort ├── README.md ├── graph.rb ├── node.rb ├── test.rb └── topological_sort.rb /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is some of the code that I wrote while studying some famous graph 4 | algorithms. Although each directory has a `README` file whith a brief 5 | explanation, it's not my intend to fully explain any of these algorithms (there 6 | are much better sources out there), I just want to show a way they can be 7 | implemented in Ruby. 8 | 9 | ## Contributing 10 | 11 | If you want to add some interesting algorithm, fix some bug or just improve my 12 | code, fell free to open a pull request. 13 | 14 | ## License 15 | 16 | Use this code as you want. If you do use it, a reference would be appreciated, but not required. 17 | 18 | 19 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/brianstorti/ruby-graph-algorithms/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 20 | -------------------------------------------------------------------------------- /bellman_ford/README.md: -------------------------------------------------------------------------------- 1 | # Bellman-ford 2 | 3 | Used to find the shortest path from a source node to every other node in any 4 | edge-weighted digraph with negative or non-negative weights. It's slower than 5 | Dijkstra's algorithm but it has the advantage of working with negative weights. 6 | Notice, though, that it won't work if the graph have a negative cycle (i.e. a 7 | cycle whose the sum of the edges' weight is a negative value, since any path 8 | could be made shorter just with another walk, over and over). 9 | -------------------------------------------------------------------------------- /bellman_ford/bellman_ford.rb: -------------------------------------------------------------------------------- 1 | class BellmanFord 2 | def initialize(graph, source_node) 3 | @graph = graph 4 | @source_node = source_node 5 | @path_to = {} 6 | @distance_to = {} 7 | 8 | compute_shortest_path 9 | end 10 | 11 | def shortest_path_to(node) 12 | path = [] 13 | while node != @source_node 14 | path.unshift(node) 15 | node = @path_to[node] 16 | end 17 | 18 | path.unshift(@source_node) 19 | end 20 | 21 | private 22 | def compute_shortest_path 23 | update_distance_of_all_edges_to(Float::INFINITY) 24 | @distance_to[@source_node] = 0 25 | 26 | @graph.nodes.size.times do 27 | @graph.edges.each do |edge| 28 | relax(edge) 29 | end 30 | end 31 | end 32 | 33 | def update_distance_of_all_edges_to(distance) 34 | @graph.nodes.each do |node| 35 | @distance_to[node] = distance 36 | end 37 | end 38 | 39 | def relax(edge) 40 | if @distance_to[edge.to] > @distance_to[edge.from] + edge.weight 41 | @distance_to[edge.to] = @distance_to[edge.from] + edge.weight 42 | @path_to[edge.to] = edge.from 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /bellman_ford/edge.rb: -------------------------------------------------------------------------------- 1 | class Edge 2 | attr_accessor :from, :to, :weight 3 | 4 | def initialize(from, to, weight) 5 | @from, @to, @weight = from, to, weight 6 | end 7 | 8 | def <=>(other) 9 | self.weight <=> other.weight 10 | end 11 | 12 | def to_s 13 | "#{from.to_s} => #{to.to_s} with weight #{weight}" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bellman_ford/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | attr_accessor :edges 4 | 5 | def initialize 6 | @nodes = [] 7 | @edges = [] 8 | end 9 | 10 | def add_node(node) 11 | nodes << node 12 | node.graph = self 13 | end 14 | 15 | def add_edge(from, to, weight) 16 | edges << Edge.new(from, to, weight) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /bellman_ford/node.rb: -------------------------------------------------------------------------------- 1 | class Node 2 | attr_accessor :name, :graph 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def adjacent_edges 9 | graph.edges.select{|e| e.from == self} 10 | end 11 | 12 | def to_s 13 | @name 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bellman_ford/spec.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require_relative 'graph' 4 | require_relative 'node' 5 | require_relative 'edge' 6 | require_relative 'bellman_ford' 7 | 8 | describe BellmanFord do 9 | before do 10 | @graph = Graph.new 11 | 12 | @graph.add_node(@node0 = Node.new("Node #0")) 13 | @graph.add_node(@node1 = Node.new("Node #1")) 14 | @graph.add_node(@node2 = Node.new("Node #2")) 15 | @graph.add_node(@node3 = Node.new("Node #3")) 16 | @graph.add_node(@node4 = Node.new("Node #4")) 17 | end 18 | 19 | it 'finds the shortest path with negative weights' do 20 | @graph.add_edge(@node0, @node1, 1) 21 | @graph.add_edge(@node0, @node4, 2) 22 | @graph.add_edge(@node1, @node2, 4) 23 | @graph.add_edge(@node2, @node4, 1) 24 | @graph.add_edge(@node2, @node3, 20) 25 | @graph.add_edge(@node3, @node4, -25) 26 | 27 | shortest_path = BellmanFord.new(@graph, @node0).shortest_path_to(@node4) 28 | 29 | shortest_path.must_equal [@node0, @node1, @node2, @node3, @node4] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /breadth_first_search/README.md: -------------------------------------------------------------------------------- 1 | # Breadth-first search (BFS) 2 | 3 | Breadth-first search (BFS) is a strategy for searching in a graph. 4 | 5 | The BFS begins at a root node and inspects all the neighboring nodes. Then for 6 | each of those neighbor nodes in turn, it inspects their neighbor nodes which 7 | were unvisited, and so on. 8 | 9 | This image shows the order in which the nodes are expanded: 10 | ![image](http://upload.wikimedia.org/wikipedia/commons/3/33/Breadth-first-tree.svg) 11 | 12 | Breadth-first search is usually used to find the shortest path between two nodes 13 | in a graph (considering only the number of edges, it won't work for weighted graphs) 14 | 15 | The steps are quite simple: 16 | * Put the source node into a queue and mark it as visited 17 | * Repeat until the queue is empty: 18 | - Remove the least recently added node n 19 | - Add each of n's unvisited neighbors to the queue and mark them as visited 20 | 21 | ## Comparing Breadth-first search and Depth-first search 22 | 23 | Both algorithms are used with the same purpose: Search a node in a graph. 24 | The BFS has the bonus advantage of finding the shortest path, while the DFS 25 | makes no guarantees about that. 26 | If finding the shortest path is not the goal, both DFS and BFS have advantages 27 | and disadvantages, depending on the data that you are looking for. 28 | A commomly used example is the search in a family tree. If the person that you are 29 | looking for is alive, then we can assume that this person (node) will be in the bottom 30 | of the graph. That means that the BFS would probably take longer to find this node 31 | than the DFS. If the person is likely closer to the top, the BFS would be more efficient 32 | is most cases. 33 | -------------------------------------------------------------------------------- /breadth_first_search/breadth_first_search.rb: -------------------------------------------------------------------------------- 1 | # Put unvisited nodes on a queue 2 | # Solves the shortest path problem: Find path from "source" to "target" 3 | # that uses the fewest number of edges 4 | # It's not recursive (like depth first search) 5 | # 6 | # The steps are quite simple: 7 | # * Put s into a FIFO queue and mark it as visited 8 | # * Repeat until the queue is empty: 9 | # - Remove the least recently added node n 10 | # - add each of n's unvisited adjacents to the queue and 11 | # mark them as visited 12 | 13 | class BreadthFirstSearch 14 | def initialize(graph, source_node) 15 | @graph = graph 16 | @node = source_node 17 | @visited = [] 18 | @edge_to = {} 19 | 20 | bfs(source_node) 21 | end 22 | 23 | def shortest_path_to(node) 24 | return unless has_path_to?(node) 25 | path = [] 26 | 27 | while(node != @node) do 28 | path.unshift(node) # unshift adds the node to the beginning of the array 29 | node = @edge_to[node] 30 | end 31 | 32 | path.unshift(@node) 33 | end 34 | 35 | private 36 | def bfs(node) 37 | # Remember, in the breadth first search we always 38 | # use a queue. In ruby we can represent both 39 | # queues and stacks as an Array, just by using 40 | # the correct methods to deal with it. In this case, 41 | # we use the "shift" method to remove an element 42 | # from the beginning of the Array. 43 | 44 | # First step: Put the source node into a queue and mark it as visited 45 | queue = [] 46 | queue << node 47 | @visited << node 48 | 49 | # Second step: Repeat until the queue is empty: 50 | # - Remove the least recently added node n 51 | # - add each of n's unvisited adjacents to the queue and mark them as visited 52 | while queue.any? 53 | current_node = queue.shift # remove first element 54 | current_node.adjacents.each do |adjacent_node| 55 | next if @visited.include?(adjacent_node) 56 | queue << adjacent_node 57 | @visited << adjacent_node 58 | @edge_to[adjacent_node] = current_node 59 | end 60 | end 61 | end 62 | 63 | # If we visited the node, so there is a path 64 | # from our source node to it. 65 | def has_path_to?(node) 66 | @visited.include?(node) 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /breadth_first_search/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | 3 | # We are dealing with an undirected graph, 4 | # so I increment the "adjacents" in both sides. 5 | # The breadth first will work the same way with 6 | # a directed graph. 7 | def add_edge(node_a, node_b) 8 | node_a.adjacents << node_b 9 | node_b.adjacents << node_a 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /breadth_first_search/node.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | 3 | class Node 4 | attr_accessor :name, :adjacents 5 | 6 | def initialize(name) 7 | # I'm using a Set instead of an Array to 8 | # avoid duplications. We don't want node1 9 | # connected to node2 twice. 10 | @adjacents = Set.new 11 | @name = name 12 | end 13 | 14 | def to_s 15 | @name 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /breadth_first_search/spec.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require_relative 'graph' 4 | require_relative 'node' 5 | require_relative 'breadth_first_search' 6 | 7 | describe BreadthFirstSearch do 8 | before do 9 | @node1 = Node.new("Node #1") 10 | @node2 = Node.new("Node #2") 11 | @node3 = Node.new("Node #3") 12 | @node4 = Node.new("Node #4") 13 | @node5 = Node.new("Node #5") 14 | end 15 | 16 | it 'finds the shortest path to a node' do 17 | graph = Graph.new 18 | graph.add_edge(@node1, @node2) 19 | graph.add_edge(@node2, @node3) 20 | graph.add_edge(@node2, @node4) 21 | graph.add_edge(@node4, @node5) 22 | graph.add_edge(@node1, @node5) 23 | 24 | path = BreadthFirstSearch.new(graph, @node1).shortest_path_to(@node5) 25 | 26 | path.must_equal [@node1, @node5] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /connected_components/README.md: -------------------------------------------------------------------------------- 1 | # Connected Components (undirected graphs) 2 | 3 | Definition: Nodes v and w are connected if there is a path between them. 4 | A connected component is a maximal set of connected vertices. 5 | 6 | Goal: Preprocess graph to answer queries of the from "is v connected to w?" in 7 | constant time. 8 | 9 | ![image](http://f.cl.ly/items/080u0w0k0S0e3O3K3t1n/connected_components.jpg) 10 | 11 | The relation "is connected to" is an equivalence relation: 12 | * Reflexive: v is connected to v 13 | * Symmetric: if v is connected to w, than w is connected to v 14 | * Transitive: if v is connected to w and w is connected to x, than v is connected to x 15 | 16 | Implementation: For each unvisited vertex, run depth-first search to identify all vertices 17 | discovered as part of the same component. 18 | -------------------------------------------------------------------------------- /connected_components/connected_components.rb: -------------------------------------------------------------------------------- 1 | class ConnectedComponent 2 | attr_accessor :connected_components 3 | 4 | def initialize(graph) 5 | @graph = graph 6 | @visited = [] 7 | @connected_components = {} 8 | 9 | counter = 0 10 | @graph.nodes.each do |node| 11 | next if @visited.include?(node) 12 | 13 | dfs(node, counter) 14 | counter += 1 15 | end 16 | end 17 | 18 | private 19 | def dfs(node, counter) 20 | @visited << node 21 | @connected_components[counter] ||= [] 22 | @connected_components[counter] << node 23 | 24 | node.adjacents.each do |adj_node| 25 | dfs(adj_node, counter) unless @visited.include?(adj_node) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /connected_components/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | 4 | def initialize 5 | @nodes = [] 6 | end 7 | 8 | def add_edge(node_a, node_b) 9 | node_a.adjacents << node_b 10 | node_b.adjacents << node_a 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /connected_components/node.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | 3 | class Node 4 | attr_accessor :name, :adjacents 5 | 6 | def initialize(name) 7 | @adjacents = Set.new 8 | @name = name 9 | end 10 | 11 | def to_s 12 | @name 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /connected_components/spec.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require_relative 'graph' 4 | require_relative 'node' 5 | require_relative 'connected_components' 6 | 7 | describe ConnectedComponent do 8 | before do 9 | @node1 = Node.new("Node #1") 10 | @node2 = Node.new("Node #2") 11 | @node3 = Node.new("Node #3") 12 | @node4 = Node.new("Node #4") 13 | @node5 = Node.new("Node #5") 14 | 15 | @graph = Graph.new 16 | 17 | @graph.nodes = [@node1, @node2, @node3, @node4, @node5] 18 | end 19 | 20 | it 'finds the connected components' do 21 | @graph.add_edge(@node1, @node2) 22 | @graph.add_edge(@node3, @node4) 23 | 24 | connected_components = ConnectedComponent.new(@graph).connected_components.values 25 | connected_components.must_include [@node1, @node2] 26 | connected_components.must_include [@node3, @node4] 27 | connected_components.must_include [@node5] 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /depth_first_search/README.md: -------------------------------------------------------------------------------- 1 | # Depth-first search (DFS) 2 | 3 | Depth-first search (DFS) is a strategy for searching in a graph. 4 | 5 | The DFS starts at the source node and explores as far as possible along each 6 | branch before backtracking. Although it finds a path from a source to a target node, 7 | if you are interested in finding the shortest path, this is not the way to go. 8 | 9 | This image shows the order in which the nodes are expanded: 10 | ![image](http://upload.wikimedia.org/wikipedia/commons/1/1f/Depth-first-tree.svg) 11 | 12 | 13 | ## Comparing Depth-first search and Breadth-first search 14 | 15 | Both algorithms are used with the same purpose: Search a node in a graph. 16 | The BFS has the bonus advantage of finding the shortest path, while the DFS 17 | makes no guarantees about that. 18 | If findind the shortest path is not the goal, both DFS and BFS have advantages 19 | and disadvantages, depending on the data that you are looking for. 20 | A commomly used example is the search in a family tree. If the person that you are 21 | looking for is alive, then we can assume that this person (node) will be in the bottom 22 | of the graph. That means that the BFS would probably take longer to find this node 23 | than the DFS. If the person is likely closer to the top, the BFS would be more efficient 24 | is most cases. 25 | -------------------------------------------------------------------------------- /depth_first_search/depth_first_search.rb: -------------------------------------------------------------------------------- 1 | class DepthFirstSearch 2 | def initialize(graph, source_node) 3 | @graph = graph 4 | @source_node = source_node 5 | @visited = [] 6 | @edge_to = {} 7 | 8 | dfs(source_node) 9 | end 10 | 11 | # After the depth-first search is done we can find 12 | # any vertice connected to "node" in constant time [O(1)] 13 | # and find a path to this node in linear time [O(n)]. 14 | def path_to(node) 15 | return unless has_path_to?(node) 16 | path = [] 17 | current_node = node 18 | 19 | while(current_node != @source_node) do 20 | path.unshift(current_node) 21 | current_node = @edge_to[current_node] 22 | end 23 | 24 | path.unshift(@source_node) 25 | end 26 | 27 | private 28 | def dfs(node) 29 | @visited << node 30 | node.adjacents.each do |adj_node| 31 | next if @visited.include?(adj_node) 32 | 33 | dfs(adj_node) 34 | @edge_to[adj_node] = node 35 | end 36 | end 37 | 38 | def has_path_to?(node) 39 | @visited.include?(node) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /depth_first_search/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | 3 | # We are dealing with an undirected graph, 4 | # so I increment the "adjacents" in both sides. 5 | # The depth first will work the same way with 6 | # a directed graph. 7 | def add_edge(node_a, node_b) 8 | node_a.adjacents << node_b 9 | node_b.adjacents << node_a 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /depth_first_search/node.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | 3 | class Node 4 | attr_accessor :name, :adjacents 5 | 6 | def initialize(name) 7 | @adjacents = Set.new 8 | @name = name 9 | end 10 | 11 | def to_s 12 | @name 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /depth_first_search/spec.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require_relative 'graph' 4 | require_relative 'node' 5 | require_relative 'depth_first_search' 6 | 7 | describe DepthFirstSearch do 8 | before do 9 | @node1 = Node.new("Node #1") 10 | @node2 = Node.new("Node #2") 11 | @node3 = Node.new("Node #3") 12 | @node4 = Node.new("Node #4") 13 | @node5 = Node.new("Node #5") 14 | end 15 | 16 | it 'finds a long path to a node when it needs to go deep in a previous adjacent node' do 17 | graph = Graph.new 18 | graph.add_edge(@node1, @node2) 19 | graph.add_edge(@node1, @node5) 20 | graph.add_edge(@node2, @node3) 21 | graph.add_edge(@node2, @node4) 22 | graph.add_edge(@node4, @node5) 23 | 24 | path = DepthFirstSearch.new(@graph, @node1).path_to(@node5) 25 | 26 | path.must_equal [@node1, @node2, @node4, @node5] 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /dijkstra/README.md: -------------------------------------------------------------------------------- 1 | # Dijkstra 2 | 3 | Used to find the shortest path from a source node to every other node in any 4 | edge-weighted digraph with non-negative weights (check the Bellman–Ford 5 | algorithm for shortest path with negative weights). 6 | 7 | Implementation: 8 | * Consider vertices in increasing order of distance from the source. 9 | * Add vertice to three and relax all edges pointing from that vertice. 10 | -------------------------------------------------------------------------------- /dijkstra/dijkstra.rb: -------------------------------------------------------------------------------- 1 | require_relative "priority_queue" 2 | 3 | class Dijkstra 4 | def initialize(graph, source_node) 5 | @graph = graph 6 | @source_node = source_node 7 | @path_to = {} 8 | @distance_to = {} 9 | @pq = PriorityQueue.new 10 | 11 | compute_shortest_path 12 | end 13 | 14 | def shortest_path_to(node) 15 | path = [] 16 | while node != @source_node 17 | path.unshift(node) 18 | node = @path_to[node] 19 | end 20 | 21 | path.unshift(@source_node) 22 | end 23 | 24 | private 25 | # This method will compute the shortest path from the source node to all the 26 | # other nodes in the graph. 27 | def compute_shortest_path 28 | update_distance_of_all_edges_to(Float::INFINITY) 29 | @distance_to[@source_node] = 0 30 | 31 | # The prioriy queue holds a node and its distance from the source node. 32 | @pq.insert(@source_node, 0) 33 | while @pq.any? 34 | node = @pq.remove_min 35 | node.adjacent_edges.each do |adj_edge| 36 | relax(adj_edge) 37 | end 38 | end 39 | end 40 | 41 | def update_distance_of_all_edges_to(distance) 42 | @graph.nodes.each do |node| 43 | @distance_to[node] = distance 44 | end 45 | end 46 | 47 | # Edge relaxation basically means that we are checking if the shortest known 48 | # path to a given node is still valid (i.e. we didn't find an even 49 | # shorter path). 50 | def relax(edge) 51 | return if @distance_to[edge.to] <= @distance_to[edge.from] + edge.weight 52 | 53 | @distance_to[edge.to] = @distance_to[edge.from] + edge.weight 54 | @path_to[edge.to] = edge.from 55 | 56 | # If the node is already in this priority queue, the only that happens is 57 | # that its distance is decreased. 58 | @pq.insert(edge.to, @distance_to[edge.to]) 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /dijkstra/edge.rb: -------------------------------------------------------------------------------- 1 | class Edge 2 | attr_accessor :from, :to, :weight 3 | 4 | def initialize(from, to, weight) 5 | @from, @to, @weight = from, to, weight 6 | end 7 | 8 | def <=>(other) 9 | self.weight <=> other.weight 10 | end 11 | 12 | def to_s 13 | "#{from.to_s} => #{to.to_s} with weight #{weight}" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /dijkstra/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | attr_accessor :edges 4 | 5 | def initialize 6 | @nodes = [] 7 | @edges = [] 8 | end 9 | 10 | def add_node(node) 11 | nodes << node 12 | node.graph = self 13 | end 14 | 15 | def add_edge(from, to, weight) 16 | edges << Edge.new(from, to, weight) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /dijkstra/node.rb: -------------------------------------------------------------------------------- 1 | class Node 2 | attr_accessor :name, :graph 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def adjacent_edges 9 | graph.edges.select{|e| e.from == self} 10 | end 11 | 12 | def to_s 13 | @name 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /dijkstra/priority_queue.rb: -------------------------------------------------------------------------------- 1 | # A very simple priority key implementation to help our Dijkstra algorithm. 2 | class PriorityQueue 3 | def initialize 4 | @queue = {} 5 | end 6 | 7 | def any? 8 | @queue.any? 9 | end 10 | 11 | def insert(key, value) 12 | @queue[key] = value 13 | order_queue 14 | end 15 | 16 | def remove_min 17 | @queue.shift.first 18 | end 19 | 20 | private 21 | def order_queue 22 | @queue.sort_by {|_key, value| value } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /dijkstra/test.rb: -------------------------------------------------------------------------------- 1 | require "pp" 2 | require_relative "graph" 3 | require_relative "node" 4 | require_relative "edge" 5 | require_relative "dijkstra" 6 | 7 | graph = Graph.new 8 | 9 | graph.add_node(node0 = Node.new("Node #0")) 10 | graph.add_node(node1 = Node.new("Node #1")) 11 | graph.add_node(node2 = Node.new("Node #2")) 12 | graph.add_node(node3 = Node.new("Node #3")) 13 | graph.add_node(node4 = Node.new("Node #4")) 14 | graph.add_node(node5 = Node.new("Node #5")) 15 | graph.add_node(node6 = Node.new("Node #6")) 16 | graph.add_node(node7 = Node.new("Node #7")) 17 | 18 | graph.add_edge(node0, node1, 5) 19 | graph.add_edge(node0, node4, 9) 20 | graph.add_edge(node0, node7, 8) 21 | graph.add_edge(node1, node2, 12) 22 | graph.add_edge(node1, node3, 15) 23 | graph.add_edge(node1, node7, 4) 24 | graph.add_edge(node2, node3, 3) 25 | graph.add_edge(node2, node6, 11) 26 | graph.add_edge(node3, node6, 9) 27 | graph.add_edge(node4, node5, 4) 28 | graph.add_edge(node4, node6, 20) 29 | graph.add_edge(node4, node7, 5) 30 | graph.add_edge(node5, node2, 1) 31 | graph.add_edge(node5, node6, 13) 32 | graph.add_edge(node7, node5, 6) 33 | graph.add_edge(node7, node2, 7) 34 | 35 | shortest_path = Dijkstra.new(graph, node0).shortest_path_to(node3) 36 | pp shortest_path.map(&:to_s) 37 | # => ["Node #0", "Node #4", "Node #5", "Node #2", "Node #3"] 38 | -------------------------------------------------------------------------------- /kosaraju_strong_components/README.md: -------------------------------------------------------------------------------- 1 | # Strong Components (Digraph) 2 | 3 | This implementation uses the Kosaraju's algorithm. 4 | 5 | Definition: Vertices v and w are considered to be strongly connected if 6 | there is a directed path from v to w **and** a directed path from w to v. 7 | 8 | ![image](http://f.cl.ly/items/3u153x2i1Y3S1h2j3H1r/strong_components.png) 9 | 10 | Strong connectivity is an equivalence relation: 11 | * v is strongly connected to v. 12 | * if v is strongly connected to w, then w is strongly connected to v. 13 | * if v is strongly connected to w, and w to x, then v is strongly connected to x. 14 | 15 | Reverse graph: Strong components in *G* are the same as in in *Gr (reverse)* 16 | -------------------------------------------------------------------------------- /kosaraju_strong_components/depth_first_order.rb: -------------------------------------------------------------------------------- 1 | class DepthFirstOrder 2 | attr_accessor :reverse_post_order, :visited 3 | 4 | def initialize(graph) 5 | @graph = graph 6 | @reverse_post_order = [] 7 | @visited = [] 8 | 9 | graph.nodes.each do |node| 10 | dfs(node) unless @visited.include?(node) 11 | end 12 | p @reverse_post_order.map(&:to_s) 13 | end 14 | 15 | private 16 | def dfs(node) 17 | @visited << node 18 | node.adjacents.each do |adj_node| 19 | dfs(adj_node) unless @visited.include?(adj_node) 20 | end 21 | 22 | @reverse_post_order << node 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /kosaraju_strong_components/edge.rb: -------------------------------------------------------------------------------- 1 | class Edge 2 | attr_accessor :from, :to 3 | 4 | def initialize(from, to) 5 | @from, @to = from, to 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /kosaraju_strong_components/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | attr_accessor :edges 4 | 5 | def initialize 6 | @nodes = [] 7 | @edges = [] 8 | end 9 | 10 | def add_node(node) 11 | nodes << node 12 | node.graph = self 13 | end 14 | 15 | def add_edge(from, to) 16 | edges << Edge.new(from, to) 17 | end 18 | 19 | def reverse! 20 | edges.each do |e| 21 | e.from, e.to = e.to, e.from 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /kosaraju_strong_components/node.rb: -------------------------------------------------------------------------------- 1 | class Node 2 | attr_accessor :name, :graph 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def adjacents 9 | graph.edges.select{|e| e.from == self}.map(&:to) 10 | end 11 | 12 | def to_s 13 | @name 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /kosaraju_strong_components/spec.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/autorun' 2 | 3 | require_relative 'graph' 4 | require_relative 'node' 5 | require_relative 'edge' 6 | require_relative 'strong_components' 7 | 8 | describe StrongComponents do 9 | before do 10 | @graph = Graph.new 11 | 12 | @graph.add_node(@node1 = Node.new('Node #1')) 13 | @graph.add_node(@node2 = Node.new('Node #2')) 14 | @graph.add_node(@node3 = Node.new('Node #3')) 15 | @graph.add_node(@node4 = Node.new('Node #4')) 16 | @graph.add_node(@node5 = Node.new('Node #5')) 17 | end 18 | 19 | it 'finds the strong components in a graph' do 20 | @graph.add_edge(@node1, @node2) 21 | @graph.add_edge(@node1, @node5) 22 | @graph.add_edge(@node2, @node3) 23 | @graph.add_edge(@node2, @node4) 24 | @graph.add_edge(@node3, @node2) 25 | @graph.add_edge(@node4, @node1) 26 | 27 | strong_components = StrongComponents.new(@graph).strong_components.values 28 | strong_components.must_include [@node3, @node2, @node1, @node4] 29 | strong_components.must_include [@node5] 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /kosaraju_strong_components/strong_components.rb: -------------------------------------------------------------------------------- 1 | require_relative "depth_first_order" 2 | 3 | # Step 1: Run depth first in the reverse graph 4 | # Step 2: Run depth first search again, considering the vertices 5 | # in the order given by the first dfs 6 | class StrongComponents 7 | attr_accessor :strong_components 8 | 9 | def initialize(graph) 10 | @graph = graph 11 | @visited = [] 12 | @strong_components = {} 13 | counter = 0 14 | 15 | graph.reverse! 16 | nodes = DepthFirstOrder.new(graph).reverse_post_order 17 | nodes.each do |node| 18 | next if @visited.include?(node) 19 | 20 | dfs(node, counter) 21 | counter += 1 22 | end 23 | end 24 | 25 | private 26 | def dfs(node, counter) 27 | @visited << node 28 | @strong_components[counter] ||= [] 29 | @strong_components[counter] << node 30 | 31 | node.adjacents.each do |adj_node| 32 | dfs(adj_node, counter) unless @visited.include?(adj_node) 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /kruskal/README.md: -------------------------------------------------------------------------------- 1 | # Kruskal algorithm 2 | 3 | The Kruskal's algorithm is used to compute the [minimum spanning tree (MST)](http://en.wikipedia.org/wiki/Minimum_spanning_tree) 4 | of a graph. It's is classified as a greedy algorithm. 5 | 6 | Implementation: First we sort the edges by weight, then we analyze the edges in 7 | this order (smallest weight first). If the nodes of the current edge 8 | are still not connected, we add this edge to the MST. This last verification 9 | ensure that we won't create a cycle. To check/connect the nodes I'm using [Union-find](http://www.algorithmist.com/index.php/Union_Find). 10 | -------------------------------------------------------------------------------- /kruskal/edge.rb: -------------------------------------------------------------------------------- 1 | class Edge 2 | attr_accessor :node1, :node2, :weight 3 | 4 | def initialize(node1, node2, weight) 5 | @node1, @node2, @weight = node1, node2, weight 6 | end 7 | 8 | def <=>(other) 9 | self.weight <=> other.weight 10 | end 11 | 12 | def to_s 13 | "#{node1.to_s} <=> #{node2.to_s} with weight #{weight}" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /kruskal/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | attr_accessor :edges 4 | 5 | def initialize 6 | @nodes = [] 7 | @edges = [] 8 | end 9 | 10 | def add_node(node) 11 | nodes << node 12 | node.graph = self 13 | end 14 | 15 | def add_edge(from, to, weight) 16 | edges << Edge.new(from, to, weight) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /kruskal/kruskal.rb: -------------------------------------------------------------------------------- 1 | require_relative "union_find" 2 | 3 | class Kruskal 4 | def compute_mst(graph) 5 | mst = [] 6 | edges = graph.edges.sort! 7 | 8 | union_find = UnionFind.new(graph.nodes) 9 | while edges.any? && mst.size <= graph.nodes.size 10 | edge = edges.shift 11 | if !union_find.connected?(edge.node1, edge.node2) 12 | union_find.union(edge.node1, edge.node2) 13 | mst << edge 14 | end 15 | end 16 | 17 | mst 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /kruskal/node.rb: -------------------------------------------------------------------------------- 1 | class Node 2 | attr_accessor :name, :graph 3 | 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | def adjacents 9 | graph.edges.select{|e| e.from == self}.map(&:to) 10 | end 11 | 12 | def to_s 13 | @name 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /kruskal/test.rb: -------------------------------------------------------------------------------- 1 | require "pp" 2 | require_relative "graph" 3 | require_relative "node" 4 | require_relative "edge" 5 | require_relative "kruskal" 6 | 7 | graph = Graph.new 8 | 9 | graph.add_node(node0 = Node.new("Node #0")) 10 | graph.add_node(node1 = Node.new("Node #1")) 11 | graph.add_node(node2 = Node.new("Node #2")) 12 | graph.add_node(node3 = Node.new("Node #3")) 13 | graph.add_node(node4 = Node.new("Node #4")) 14 | graph.add_node(node5 = Node.new("Node #5")) 15 | graph.add_node(node6 = Node.new("Node #6")) 16 | graph.add_node(node7 = Node.new("Node #7")) 17 | 18 | graph.add_edge(node0, node7, 16) 19 | graph.add_edge(node2, node3, 17) 20 | graph.add_edge(node1, node7, 19) 21 | graph.add_edge(node0, node2, 26) 22 | graph.add_edge(node5, node7, 28) 23 | graph.add_edge(node1, node3, 29) 24 | graph.add_edge(node1, node5, 32) 25 | graph.add_edge(node2, node7, 34) 26 | graph.add_edge(node4, node5, 35) 27 | graph.add_edge(node1, node2, 36) 28 | graph.add_edge(node4, node7, 37) 29 | graph.add_edge(node0, node4, 38) 30 | graph.add_edge(node6, node2, 40) 31 | 32 | pp Kruskal.new.compute_mst(graph).map(&:to_s) 33 | -------------------------------------------------------------------------------- /kruskal/union_find.rb: -------------------------------------------------------------------------------- 1 | class UnionFind 2 | def initialize(nodes) 3 | @unions = {} 4 | nodes.each_with_index do |node, index| 5 | @unions[node] = index 6 | end 7 | end 8 | 9 | def connected?(node1, node2) 10 | @unions[node1] == @unions[node2] 11 | end 12 | 13 | def union(node1, node2) 14 | return if connected?(node1, node2) 15 | node1_id = @unions[node1] 16 | node2_id = @unions[node2] 17 | 18 | @unions.each do |node, id| 19 | @unions[node] = node1_id if id == node2_id 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /topological_sort/README.md: -------------------------------------------------------------------------------- 1 | # Topological sort (DAGs) 2 | 3 | Goal: Given a set of tasks to be completed with precedence constraints, in 4 | which order should we schedule the tasks. In this example application, the 5 | vertices would be the tasks and the edges direction the precedence constraints. 6 | 7 | It's important to notice that the topological sort works just for DAGs 8 | (directed acyclic graphs). 9 | 10 | Implementation: The implementation is pretty 11 | straightforward. I just run a recursive depth-first 12 | sort for each unvisited vertice in the graph and when 13 | the stack is done, I add the vertice to my topological 14 | ordered list. 15 | -------------------------------------------------------------------------------- /topological_sort/graph.rb: -------------------------------------------------------------------------------- 1 | class Graph 2 | attr_accessor :nodes 3 | 4 | def initialize 5 | @nodes = [] 6 | end 7 | 8 | def add_edge(from, to) 9 | from.adjacents << to 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /topological_sort/node.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | 3 | class Node 4 | attr_accessor :name, :adjacents 5 | 6 | def initialize(name) 7 | # I'm using a Set instead of an Array to 8 | # avoid duplications. We don't want node1 9 | # connected to node2 twice. 10 | @adjacents = Set.new 11 | @name = name 12 | end 13 | 14 | def to_s 15 | @name 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /topological_sort/test.rb: -------------------------------------------------------------------------------- 1 | require_relative "graph" 2 | require_relative "node" 3 | require_relative "topological_sort" 4 | 5 | graph = Graph.new 6 | 7 | graph.nodes << (node1 = Node.new("Node #1")) 8 | graph.nodes << (node2 = Node.new("Node #2")) 9 | graph.nodes << (node3 = Node.new("Node #3")) 10 | graph.nodes << (node4 = Node.new("Node #4")) 11 | graph.nodes << (node5 = Node.new("Node #5")) 12 | 13 | 14 | graph.add_edge(node1, node2) 15 | graph.add_edge(node1, node5) 16 | graph.add_edge(node2, node3) 17 | graph.add_edge(node2, node4) 18 | 19 | p TopologicalSort.new(graph).post_order.map(&:to_s) 20 | # "Node #3", "Node #4", "Node #2", "Node #5", "Node #1" 21 | -------------------------------------------------------------------------------- /topological_sort/topological_sort.rb: -------------------------------------------------------------------------------- 1 | # Topological sort works only for DAGs (Directed acyclic graphs). 2 | # The objective of this sort is do redraw the DAG so all edged 3 | # point upwards. 4 | class TopologicalSort 5 | attr_accessor :post_order 6 | 7 | def initialize(graph) 8 | @post_order = [] 9 | @visited = [] 10 | 11 | graph.nodes.each do |node| 12 | dfs(node) unless @visited.include?(node) 13 | end 14 | end 15 | 16 | private 17 | def dfs(node) 18 | @visited << node 19 | node.adjacents.each do |adj_node| 20 | dfs(adj_node) unless @visited.include?(adj_node) 21 | end 22 | 23 | @post_order << node 24 | end 25 | end 26 | --------------------------------------------------------------------------------