ogr 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 83cf5471104a54bfb6ed4f87203f21af7b6c0dc1
4
- data.tar.gz: ecf312736e5b4a2a950fed9157d563625ff2d266
3
+ metadata.gz: ffca6a82d71e20952e078e9805c5ad30bc1bdc73
4
+ data.tar.gz: 8c1e23fc9bfed9e0c4d798318bff2f8f23962307
5
5
  SHA512:
6
- metadata.gz: 7c6a05934278805233e4a72479bd1aa6e1f8d172a25a5498da9c714acf61b9391723205d7a105a2910b87821408d3b9fc9c909ed1bb56fc2d72c23c463c7a5dc
7
- data.tar.gz: 1c56b4b496bb8c60b237599c7c8d06597f5620fda246804f9b097114997ff1a4662b19d0d8e519252c750d6e9f3ddf51e3e2647cc09a6c00d0ecbfafc99253d3
6
+ metadata.gz: d22658bd055558b8678099d3188ac4cfa1dbc8c318fe9b0ca2cdb9d71c8ab436e88eeb4e23f5031cfae0750723188b5c4e37febfa84c40cc344ecb0c0222fff8
7
+ data.tar.gz: 34d97eeaaad02a14e25a129154e47ceefeca43c8af16525ec9fb6eb067660db59dc4ea3084a55eb5b26f5a50e7877001800be076d768968352f9a4c89b83ae42
data/.rubocop.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  Metrics/LineLength:
2
2
  Max: 100
3
3
 
4
+ Lint/AssignmentInCondition:
5
+ Enabled: false
data/.travis.yml CHANGED
@@ -1,10 +1,19 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.2.2
4
+ - 2.1.5
5
5
  - 2.2.3
6
6
  - 2.3.0
7
7
  - jruby-head
8
+ env:
9
+ - BENCH=false
10
+ - BENCH=true
11
+ matrix:
12
+ exclude:
13
+ rvm: jruby-head
14
+ env: BENCH=true
15
+ allow_failures:
16
+ - env: BENCH=true
8
17
  script:
9
18
  - bundle exec rake
10
19
  before_install:
data/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Ogr
2
2
 
3
- General graph library for Ruby. Provides sparse(or dense), directed(or undirected) and weighted graphs for Ruby.
3
+ [![Build Status](https://travis-ci.org/knife/ogr.svg?branch=master)](https://travis-ci.org/knife/ogr)
4
+ [![Code Climate](https://codeclimate.com/github/knife/ogr/badges/gpa.svg)](https://codeclimate.com/github/knife/ogr)
5
+
6
+ General graph library for Ruby. Provides sparse(or dense), directed(or undirected) and weighted(or normal) graphs.
7
+ Graph processing algorithms like BFS, DFS, ShortestPaths, MinimumSpanningTree are also included.
8
+ This gem depends only on [DS Gem](https://github.com/knife/ds) which contains other data structures
9
+ not implemented in Ruby Standard Library.
4
10
 
5
11
  ## Installation
6
12
 
@@ -15,7 +21,7 @@ General graph library for Ruby. Provides sparse(or dense), directed(or undirecte
15
21
  graph = Ogr::Graph.new
16
22
  ```
17
23
 
18
- To not have to type "Ogr::" before each class, use:
24
+ To not have to type "Ogr::" before each class:
19
25
 
20
26
  ```ruby
21
27
  include Ogr
@@ -25,47 +31,66 @@ To not have to type "Ogr::" before each class, use:
25
31
 
26
32
  ## Graph
27
33
 
28
- Creating new Graph
34
+ Creating new Graph is easy.
29
35
 
30
- First define edges:
36
+ First define edges(weights are optional):
31
37
 
32
38
  ```ruby
33
39
  edges = []
34
- edges << Edge.new('Jim','Bob')
35
- edges << Edge.new('Jim','Tom')
36
- edges << Edge.new('Bob','Jack')
37
- edges << Edge.new('Tom','Bob')
40
+ edges << Edge.new('Jim','Bob', 12)
41
+ edges << Edge.new('Jim','Tom', 3)
42
+ edges << Edge.new('Bob','Jack', 8)
43
+ edges << Edge.new('Tom','Bob', 5)
38
44
  ```
39
45
  or
40
46
 
41
47
  ```ruby
42
48
  edges = [
43
- ['Jim','Bob'],
44
- ['Jim','Tom'],
45
- ['Bob','Jack'],
46
- ['Tom','Bob']
49
+ ['Jim','Bob', 12],
50
+ ['Jim','Tom', 3],
51
+ ['Bob','Jack', 8],
52
+ ['Tom','Bob', 5]
47
53
  ]
48
54
  ```
49
55
 
50
- New undirected graph (implemented as adjency list)
56
+ Next create new graph:
51
57
 
52
58
  ```ruby
53
59
  graph = Graph.new(edges)
54
60
  ```
55
61
 
56
- New undirected dense graph (implemented as triangular matrix)
62
+ By default graphs are implemented as adjacency lists. This implementation works in most situations.
63
+ However, sometimes triangular matrix may be better choice for internal graph representation (for e.g. dense graphs).
64
+ You can set graph internal representation by passing second argument to initializer or using special constructor.
57
65
 
58
66
  ```ruby
59
- graph = Graph.create(edges)
67
+ graph = Graph.new_dense(edges)
60
68
  ```
61
69
  or
62
70
 
63
71
  ```ruby
64
72
  graph = Graph.new(edges, :tri_matrix)
65
-
66
73
  ```
67
74
 
75
+
76
+ ### Graph API
77
+
78
+ Some methods defined on graph object:
79
+
80
+ * degree
81
+ * edge?
82
+ * add_edge
83
+ * add_edges
84
+ * get_edge
85
+ * vertex_size
86
+ * remove
87
+ * add
88
+ * neighbors
89
+ * vertexes
90
+ * edges
91
+
68
92
  Examples:
93
+
69
94
  ```ruby
70
95
  graph.vertex_size #=> 4
71
96
  graph.degree("Bob") #=> 3
@@ -74,19 +99,22 @@ Examples:
74
99
  graph.add_edge(Edge.new("Bob", "Kate"))
75
100
  graph.remove("Bob", "Jack")
76
101
  graph.neighbors('Tom') #=> ["Bob", "Jim"]
102
+ graph.add("Bob", "Jack")
77
103
  ```
78
104
 
79
- Iterating
105
+ Iterators:
80
106
 
81
107
  ```ruby
82
- graph.each_edge{ |e| p e }
83
- graph.each_vertex{ |v| p v }
108
+ graph.each_edge { |e| p e }
109
+ graph.each_vertex { |v| p v }
84
110
  ```
85
111
 
86
112
 
87
113
  ## Digraph
88
114
 
89
- Directed Weighted Graph
115
+ Digraph (Directed Graph) is graph with directed edges.
116
+
117
+ Creating new directed graph (implemented as adjacency list):
90
118
 
91
119
  ```ruby
92
120
  edges = []
@@ -104,38 +132,94 @@ Directed Weighted Graph
104
132
  edges << Edge.new(:F, :B, 7)
105
133
  ```
106
134
 
107
- New directed graph (implemented as adjency list)
108
-
109
135
  ```ruby
110
- wdigraph = Digraph.new(edges)
136
+ digraph = Digraph.new(edges)
111
137
  ```
112
138
 
113
- New directed dense graph (implemented as matrix)
139
+ New directed dense graph (implemented as matrix):
114
140
  ```ruby
115
- wdigraph = Digraph.create(edges)
141
+ digraph = Digraph.new_dense(edges)
116
142
  ```
117
143
  or
118
144
  ```ruby
119
- wdigraph = Digraph.new(edges, :matrix)
145
+ digraph = Digraph.new(edges, :matrix)
120
146
  ```
121
147
 
148
+ ### Digraph API
149
+
150
+ Digraph inherits from Graph so all methods defined in Graph are available.
151
+ Digraph defines some additional methods specific to directed graphs:
152
+
153
+ * in_degree
154
+ * out_degree
155
+
122
156
  Examples
123
157
  ```ruby
124
- wdigraph.get_edge(:D, :C).weight #=> 11
125
- wdigraph.degree(:E) #=> 4
126
- wdigraph.in_degree(:E) #=> 3
127
- wdigraph.out_degree(:E) #=> 1
158
+ digraph.get_edge(:D, :C).weight #=> 11
159
+ digraph.degree(:E) #=> 4
160
+ digraph.in_degree(:E) #=> 3
161
+ digraph.out_degree(:E) #=> 1
128
162
  ```
129
163
 
130
164
  ## Searching
131
165
 
132
166
  Breadth First Search:
133
167
  ```ruby
134
- BreadthFirstSearch.new(wdigraph).search(:A) #=> [:A, :C, :D, :G, :E, :F, :B]
168
+ BreadthFirstSearch.new(digraph).search(:A) #=> [:A, :C, :D, :G, :E, :F, :B]
135
169
  ```
136
170
 
137
171
  Depth First Search:
138
172
  ```ruby
139
- DepthFirstSearch.new(wdigraph).search(:A) #=> [:A, :G, :B, :E, :D, :C, :F]
140
- `
173
+ DepthFirstSearch.new(digraph).search(:A) #=> [:A, :G, :B, :E, :D, :C, :F]
174
+ ```
175
+
176
+ You can also pass block to search methods:
177
+ ```ruby
178
+ DepthFirstSearch.new(digraph).search(:A) { |v| v.to_s.downcase }
179
+ ```
180
+
181
+ If source vertex is not given, search method iterates over all vertexes in graph.
182
+
183
+ ## Topological Sort
184
+
185
+ Topological Sort sorts vertexes in topological order (all edges in graph points in the same direction).
186
+ Works only for digraphs without cycles (Directed Acyclic Graphs)
187
+
188
+ ```ruby
189
+ TopologicalSort.new(graph).sort #=> array of vertexes in topological order
190
+ ```
191
+
192
+ ## Connected Compontents
193
+
194
+ ConnectedComponents finds all connected components in graph.
195
+
196
+ ```ruby
197
+ cc = ConnectedComponents.new(graph)
198
+ cc.count # => 1
199
+ cc.connected?(:a, :b) # => true
200
+ ```
201
+
202
+ ## Minimum Spanning Tree
203
+
204
+ MinimumSpanningTree finds tree connecting all vertexes in graph with minimal weights.
205
+ Implements Kruskal's algorithm.
206
+
207
+ ```ruby
208
+ tree = MinimumSpanningTree.new(graph).calculate #=> array of edges
209
+ ```
210
+
211
+
212
+ ## Shortest Paths
213
+
214
+ Finds shortest paths from source to all vertexes in graph. Implements Dijkstra's algorithm and
215
+ works only for digraphs with positive weights.
216
+
217
+ Finding shortest paths in graph from vertex 0:
218
+ ```ruby
219
+ sp = ShortestPaths.new(digraph, 0)
220
+ sp.distance_to(7) #=> returns shortest distance from vertex 0 to vertex 7
221
+ sp.has_path?(7) #=> returns true if shortest path to 7 exists
222
+ sp.path_to(4) #=> returns path from vertex 0 to vertex 4 (array of vertexes)
223
+ ```
224
+
141
225
 
data/lib/ogr.rb CHANGED
@@ -9,8 +9,13 @@ require 'ogr/graphs/graph_as_tri_matrix'
9
9
  require 'ogr/graphs/edge_bag'
10
10
  require 'ogr/graphs/graph'
11
11
  require 'ogr/graphs/digraph'
12
- require 'ogr/graphs/breadth_first_search'
13
- require 'ogr/graphs/depth_first_search'
12
+ require 'ogr/breadth_first_search'
13
+ require 'ogr/depth_first_search'
14
+ require 'ogr/topological_sort'
15
+ require 'ogr/connected_components'
16
+ require 'ogr/shortest_pahts'
17
+ require 'ogr/minimum_spanning_tree'
18
+ require 'ogr/union_find'
14
19
 
15
20
  # Main module namespace
16
21
  module Ogr
@@ -1,27 +1,22 @@
1
1
  module Ogr
2
2
  # Class implements Breadth First Search in graphs
3
3
  class BreadthFirstSearch
4
- attr_accessor :parents, :visited, :distance
5
- private :parents=, :visited=, :distance=
4
+ attr_reader :parents, :visited, :distance
6
5
 
7
6
  def initialize(graph)
8
7
  @graph = graph
9
- @colors = {}
8
+ @colors = Hash.new(:white)
10
9
  @parents = {}
11
10
  @visited = []
12
- @distance = {}
11
+ @distance = Hash.new(Float::INFINITY)
13
12
  @to_visit = SimpleQueue.new
14
13
  end
15
14
 
16
- def search(s)
17
- # TODO: Check if source exists in graph
18
- reset!
19
- visit_source(s)
20
- until to_visit.empty?
21
- v = to_visit.dequeue
22
- visit_neighbors(v)
23
- colors[v] = :black
24
- visited << (block_given? ? yield(v) : v)
15
+ def search(source = nil, &block)
16
+ if source
17
+ visit_source(source, &block)
18
+ else
19
+ graph.each_vertex { |v| visit_source(v, &block) unless visited?(v) }
25
20
  end
26
21
  visited
27
22
  end
@@ -30,20 +25,21 @@ module Ogr
30
25
 
31
26
  attr_accessor :graph, :to_visit, :colors
32
27
 
33
- def reset!
34
- self.visited = []
35
- graph.each_vertex do |v|
36
- colors[v] = :white
37
- parents[v] = nil
38
- distance[v] = Float::INFINITY
39
- end
40
- end
41
-
42
28
  def visit_source(s)
43
- colors[s] = :white
44
- parents[s] = nil
45
29
  distance[s] = 0
46
30
  to_visit.enqueue s
31
+ until to_visit.empty?
32
+ v = to_visit.dequeue
33
+ visit_neighbors(v)
34
+ colors[v] = :black
35
+ visited << (block_given? ? yield(v) : v)
36
+ end
37
+ end
38
+
39
+ def visit_neighbors(u)
40
+ graph.neighbors(u).each do |v|
41
+ visit_node(v, u) unless visited?(v)
42
+ end
47
43
  end
48
44
 
49
45
  def visit_node(v, from)
@@ -53,14 +49,8 @@ module Ogr
53
49
  to_visit.enqueue v
54
50
  end
55
51
 
56
- def visit_neighbors(u)
57
- graph.neighbors(u).each do |v|
58
- visit_node(v, u) if not_visited?(v)
59
- end
60
- end
61
-
62
- def not_visited?(v)
63
- colors[v] == :white
52
+ def visited?(v)
53
+ colors[v] != :white
64
54
  end
65
55
  end
66
56
  end
@@ -0,0 +1,58 @@
1
+ module Ogr
2
+ # ConnectedComponents class finds graph components
3
+ class ConnectedComponents
4
+ attr_reader :graph, :visited, :counter, :stack
5
+ private :graph, :visited, :counter, :stack
6
+
7
+ def initialize(graph)
8
+ @graph = graph
9
+ @visited = {}
10
+ @counter = 0
11
+ find_components
12
+ end
13
+
14
+ def connected?(v, u)
15
+ visited[v] == visited[u]
16
+ end
17
+
18
+ def component(v)
19
+ visited[v]
20
+ end
21
+
22
+ def components
23
+ visited
24
+ end
25
+
26
+ def count
27
+ counter
28
+ end
29
+
30
+ private
31
+
32
+ def find_components
33
+ vertexes.each do |v|
34
+ unless visited[v]
35
+ dfs(v)
36
+ @counter += 1
37
+ end
38
+ end
39
+ end
40
+
41
+ def dfs(v)
42
+ @stack = [v]
43
+ while u = stack.pop
44
+ visit(u)
45
+ end
46
+ end
47
+
48
+ def visit(v)
49
+ return if visited[v]
50
+ visited[v] = counter
51
+ stack.concat(graph.neighbors(v))
52
+ end
53
+
54
+ def vertexes
55
+ @vertexes ||= graph.vertexes
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,38 @@
1
+ module Ogr
2
+ # Class implements Depth First Search in graphs
3
+ class DepthFirstSearch
4
+ attr_reader :graph, :stack, :marked, :visited
5
+ private :graph, :stack, :marked
6
+
7
+ def initialize(graph)
8
+ @graph = graph
9
+ @marked = {}
10
+ @visited = []
11
+ end
12
+
13
+ def search(source = nil, &block)
14
+ if source
15
+ dfs(source, &block)
16
+ else
17
+ graph.vertexes.each { |v| dfs(v, &block) unless marked[v] }
18
+ end
19
+ visited
20
+ end
21
+
22
+ private
23
+
24
+ def dfs(v, &block)
25
+ @stack = [v]
26
+ while u = stack.pop
27
+ visit(u, &block)
28
+ end
29
+ end
30
+
31
+ def visit(v)
32
+ return if marked[v]
33
+ visited << (block_given? ? yield(v) : v)
34
+ marked[v] = true
35
+ stack.concat(graph.neighbors(v))
36
+ end
37
+ end
38
+ end
@@ -12,6 +12,7 @@ module Ogr
12
12
  add_edges(edges)
13
13
  end
14
14
 
15
+ # Creates new graph implemented as matrix.
15
16
  def self.new_dense(args)
16
17
  new(args, :matrix)
17
18
  end
@@ -24,15 +24,16 @@ module Ogr
24
24
  new(edges, :tri_matrix)
25
25
  end
26
26
 
27
- # Adds new edge to graph.
27
+ # Creates new edge in graph.
28
28
  def add(x, y, weight = 1)
29
+ weight ||= 1
29
30
  @g.add(push(x), push(y), weight)
30
31
  end
31
32
 
32
33
  # Adds new edges to graph.
33
34
  def add_edges(edges)
34
35
  if edges[0].is_a? Array
35
- edges.each { |e| add(e[0], e[1]) }
36
+ edges.each { |e| add(e[0], e[1], e[2]) }
36
37
  else
37
38
  edges.each { |e| add(e.from, e.to, e.weight) }
38
39
  end
@@ -70,34 +71,45 @@ module Ogr
70
71
  Edge.new(x, y, w) if w
71
72
  end
72
73
 
73
- def vc
74
- (0...vertexes.size)
74
+ # Vertex iterator
75
+ def each_vertex
76
+ vc.each { |v| yield vertexes[v] }
75
77
  end
76
78
 
79
+ # Edge iterator
80
+ def each_edge(&block)
81
+ @g.each_edge(vertexes, &block)
82
+ end
83
+
84
+ # Returns array of all graph vertexes
77
85
  def vertexes
78
86
  @map.to_a
79
87
  end
80
88
 
89
+ # Vertex size
81
90
  def vertex_size
82
91
  vertexes.size
83
92
  end
84
93
 
85
- # Vertex iterator
86
- def each_vertex
87
- vc.each { |v| yield vertexes[v] }
94
+ # Vertex numbers
95
+ def vc
96
+ (0...vertexes.size)
88
97
  end
89
98
 
90
- # Edge iterator
91
- def each_edge(&block)
92
- @g.each_edge(vertexes, &block)
99
+ # Returns array of all graph edges
100
+ def edges
101
+ arr = []
102
+ each_edge { |e| arr << e }
103
+ arr
93
104
  end
94
105
 
95
- private
96
-
106
+ # Internal numeric representation of vertex
97
107
  def index(x)
98
108
  @map.index(x)
99
109
  end
100
110
 
111
+ private
112
+
101
113
  def push(x)
102
114
  @map.push(x)
103
115
  end
@@ -37,6 +37,7 @@ module Ogr
37
37
 
38
38
  # Returns all neighbors for given vertex.
39
39
  def neighbors(x)
40
+ return [] unless @store[x]
40
41
  @store[x].map { |edge| edge[:v] }
41
42
  end
42
43
 
@@ -44,8 +45,7 @@ module Ogr
44
45
  # edges, :out - outcoming edges, :all - incoming and outcoming edges.
45
46
  def degree(x, direction = :all)
46
47
  r = Hash.new(0)
47
- vc = (0..@store.size)
48
- vc.each do |i|
48
+ (0..@store.size).each do |i|
49
49
  r[:in] += 1 if connected?(i, x)
50
50
  r[:out] += 1 if connected?(x, i)
51
51
  end
@@ -0,0 +1,32 @@
1
+ module Ogr
2
+ # Class implements Kruskal algorithm for finding Minimum Spanning Tree
3
+ class MinimumSpanningTree
4
+ attr_reader :graph, :uf
5
+ private :graph, :uf
6
+
7
+ def initialize(graph)
8
+ @graph = graph
9
+ @uf = UnionFind.new(@graph.vertexes.size)
10
+ end
11
+
12
+ def calculate
13
+ tree = []
14
+ while edge = edges.shift
15
+ from = graph.index(edge.from)
16
+ to = graph.index(edge.to)
17
+
18
+ unless uf.connected?(from, to)
19
+ uf.union(from, to)
20
+ tree << edge
21
+ end
22
+ end
23
+ tree
24
+ end
25
+
26
+ private
27
+
28
+ def edges
29
+ @edges ||= @graph.edges.sort_by(&:weight)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,63 @@
1
+ module Ogr
2
+ # Finds shortest paths in graph using Dijkstra algorithm
3
+ class ShortestPaths
4
+ attr_reader :parent, :distance, :graph, :queue
5
+ private :parent, :distance, :graph, :queue
6
+
7
+ def initialize(graph, source)
8
+ @graph = graph
9
+ @queue = IndexedPriorityQueue.min
10
+ @parent = {}
11
+ @distance = {}
12
+ @graph.vertexes.each { |v| @distance[v] = Float::INFINITY }
13
+
14
+ find_paths(source)
15
+ end
16
+
17
+ def distance_to(goal)
18
+ distance[goal]
19
+ end
20
+
21
+ def from(goal)
22
+ parent[goal]
23
+ end
24
+
25
+ def path?(goal)
26
+ distance[goal] < Float::INFINITY
27
+ end
28
+
29
+ def path_to(goal)
30
+ v = goal
31
+ path = [v]
32
+ path.push v while v = parent[v]
33
+ path.reverse
34
+ end
35
+
36
+ def find_paths(source)
37
+ distance[source] = 0
38
+ queue.push(source, 0)
39
+
40
+ while v = queue.shift
41
+ graph.neighbors(v).each do |u|
42
+ relax(graph.get_edge(v, u))
43
+ end
44
+ end
45
+ end
46
+
47
+ def relax(edge)
48
+ new_distance = distance[edge.from] + edge.weight
49
+ found_better(edge, new_distance) if new_distance < distance[edge.to]
50
+ end
51
+
52
+ def found_better(edge, new_distance)
53
+ v = edge.to
54
+ distance[v] = new_distance
55
+ parent[v] = edge.from
56
+ if queue.include?(v)
57
+ queue.update(v, new_distance)
58
+ else
59
+ queue.push(v, new_distance)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,28 @@
1
+ module Ogr
2
+ # Sorts graph vertexes in topological order
3
+ class TopologicalSort
4
+ attr_reader :graph, :sorted, :marked
5
+ private :graph, :sorted, :marked
6
+
7
+ def initialize(graph)
8
+ @graph = graph
9
+ @marked = {}
10
+ @sorted = []
11
+ end
12
+
13
+ def sort
14
+ graph.vertexes.each { |v| dfs(v) unless marked[v] }
15
+ sorted.reverse
16
+ end
17
+
18
+ private
19
+
20
+ def dfs(v)
21
+ marked[v] = true
22
+ graph.neighbors(v).reverse_each do |u|
23
+ dfs(u) unless marked[u]
24
+ end
25
+ sorted.push v
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ module Ogr
2
+ # Class implements Union Find algorithm
3
+ class UnionFind
4
+ attr_reader :store, :sizes
5
+ private :store, :sizes
6
+
7
+ def initialize(n)
8
+ @store = (0..n).to_a
9
+ @sizes = [1] * n
10
+ end
11
+
12
+ def connected?(x, y)
13
+ root(x) == root(y)
14
+ end
15
+
16
+ def union(x, y)
17
+ root_x = root(x)
18
+ root_y = root(y)
19
+ if sizes[root_y] > sizes[root_x]
20
+ update_sizes(root_x, root_y)
21
+ else
22
+ update_sizes(root_y, root_x)
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def update_sizes(x, y)
29
+ store[x] = y
30
+ sizes[y] += sizes[x]
31
+ end
32
+
33
+ def root(x)
34
+ parent = store[x]
35
+ parent = store[parent] while parent != store[parent]
36
+ parent
37
+ end
38
+ end
39
+ end
data/lib/ogr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ogr
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/ogr.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ['lib']
31
31
 
32
- spec.add_runtime_dependency 'ds', '~> 0.0'
32
+ spec.add_runtime_dependency 'ds', '>= 0.0.7'
33
33
 
34
34
  spec.add_development_dependency 'bundler', '~> 1.11'
35
35
  spec.add_development_dependency 'rake', '~> 10.0'
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ogr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - knife
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-09-02 00:00:00.000000000 Z
11
+ date: 2016-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ds
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0.0'
19
+ version: 0.0.7
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0.0'
26
+ version: 0.0.7
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -83,8 +83,9 @@ files:
83
83
  - bin/console
84
84
  - bin/setup
85
85
  - lib/ogr.rb
86
- - lib/ogr/graphs/breadth_first_search.rb
87
- - lib/ogr/graphs/depth_first_search.rb
86
+ - lib/ogr/breadth_first_search.rb
87
+ - lib/ogr/connected_components.rb
88
+ - lib/ogr/depth_first_search.rb
88
89
  - lib/ogr/graphs/digraph.rb
89
90
  - lib/ogr/graphs/edge.rb
90
91
  - lib/ogr/graphs/edge_bag.rb
@@ -92,6 +93,10 @@ files:
92
93
  - lib/ogr/graphs/graph_as_list.rb
93
94
  - lib/ogr/graphs/graph_as_matrix.rb
94
95
  - lib/ogr/graphs/graph_as_tri_matrix.rb
96
+ - lib/ogr/minimum_spanning_tree.rb
97
+ - lib/ogr/shortest_pahts.rb
98
+ - lib/ogr/topological_sort.rb
99
+ - lib/ogr/union_find.rb
95
100
  - lib/ogr/version.rb
96
101
  - ogr.gemspec
97
102
  homepage: https://github.com/knife/ogr
@@ -1,40 +0,0 @@
1
- module Ogr
2
- # Class implements Breadth First Search in graphs
3
- class DepthFirstSearch
4
- attr_accessor :parents, :visited, :distance, :marked
5
- private :parents=, :visited=, :distance=, :marked=
6
-
7
- def initialize(graph)
8
- @graph = graph
9
- @parents = {}
10
- @marked = {}
11
- @visited = []
12
- @distance = {}
13
- end
14
-
15
- def search(u, &block)
16
- reset!
17
- dfs(u, &block)
18
- visited
19
- end
20
-
21
- def dfs(v, from = nil, &block)
22
- marked[v] = true
23
- visited << (block_given? ? yield(v) : v)
24
- parents[v] = from
25
- graph.neighbors(v).reverse_each do |u|
26
- dfs(u, v, &block) unless marked[u]
27
- end
28
- end
29
-
30
- private
31
-
32
- attr_reader :graph
33
-
34
- def reset!
35
- self.visited = []
36
- self.parents = {}
37
- self.marked = {}
38
- end
39
- end
40
- end