algorithms 0.0.1 → 0.1.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.
Files changed (54) hide show
  1. data/History.txt +139 -2
  2. data/Manifest +31 -8
  3. data/README +90 -0
  4. data/Rakefile +22 -9
  5. data/algorithms.gemspec +28 -101
  6. data/benchmark.rb +29 -27
  7. data/benchmarks/rbench.rb +16 -0
  8. data/benchmarks/rbench/column.rb +26 -0
  9. data/benchmarks/rbench/group.rb +43 -0
  10. data/benchmarks/rbench/report.rb +53 -0
  11. data/benchmarks/rbench/runner.rb +109 -0
  12. data/benchmarks/rbench/summary.rb +51 -0
  13. data/benchmarks/sorts.rb +33 -0
  14. data/ext/containers/bst/bst.c +205 -0
  15. data/ext/containers/{priority_queue → bst}/extconf.rb +1 -1
  16. data/ext/containers/deque/deque.c +233 -0
  17. data/ext/containers/deque/extconf.rb +4 -0
  18. data/ext/containers/tree_map/extconf.rb +1 -1
  19. data/ext/containers/tree_map/rbtree.c +73 -25
  20. data/lib/algorithms.rb +65 -6
  21. data/lib/algorithms/search.rb +84 -0
  22. data/lib/algorithms/sort.rb +238 -0
  23. data/lib/containers/deque.rb +176 -0
  24. data/lib/containers/heap.rb +451 -111
  25. data/lib/containers/kd_tree.rb +87 -0
  26. data/lib/containers/priority_queue.rb +107 -508
  27. data/lib/containers/queue.rb +62 -23
  28. data/lib/containers/rb_tree_map.rb +398 -0
  29. data/lib/containers/splay_tree_map.rb +274 -0
  30. data/lib/containers/stack.rb +59 -21
  31. data/lib/containers/suffix_array.rb +68 -0
  32. data/lib/containers/trie.rb +182 -0
  33. data/lib/graphs/graph.rb +25 -0
  34. data/spec/bst_spec.rb +31 -0
  35. data/spec/deque_spec.rb +108 -0
  36. data/spec/heap_spec.rb +111 -66
  37. data/spec/kd_tree_spec.rb +89 -0
  38. data/spec/priority_queue_spec.rb +71 -27
  39. data/spec/queue_spec.rb +53 -45
  40. data/spec/rb_tree_map_spec.rb +123 -0
  41. data/spec/search_spec.rb +28 -0
  42. data/spec/sort_spec.rb +28 -0
  43. data/spec/splay_tree_map_spec.rb +102 -0
  44. data/spec/stack_spec.rb +56 -49
  45. data/spec/suffix_array_spec.rb +40 -0
  46. data/spec/trie_spec.rb +59 -0
  47. metadata +54 -32
  48. data/README.txt +0 -58
  49. data/ext/containers/priority_queue/priority_queue.c +0 -948
  50. data/ext/containers/tree_map/Rakefile +0 -4
  51. data/lib/containers/hash.rb +0 -0
  52. data/lib/containers/tree_map.rb +0 -265
  53. data/spec/priority_queue_test.rb +0 -371
  54. data/spec/tree_map_spec.rb +0 -99
@@ -0,0 +1,274 @@
1
+ require 'containers/stack'
2
+ =begin rdoc
3
+ A SplayTreeMap is a map that is stored in ascending order of its keys, determined by applying
4
+ the function <=> to compare keys. No duplicate values for keys are allowed, so new values of a key
5
+ overwrites the old value of the key.
6
+
7
+ A major advantage of SplayTreeMap over a Hash is the fact that keys are stored in order and can thus be
8
+ iterated over in order. Also, Splay Trees are self-optimizing as recently accessed nodes stay near
9
+ the root and are easily re-accessed later. Splay Trees are also more simply implemented than Red-Black
10
+ trees.
11
+
12
+ Splay trees have amortized O(log n) performance for most methods, but are O(n) worst case. This happens
13
+ when keys are added in sorted order, causing the tree to have a height of the number of items added.
14
+
15
+ =end
16
+ class Containers::SplayTreeMap
17
+ include Enumerable
18
+
19
+ # Create and initialize a new empty SplayTreeMap.
20
+ def initialize
21
+ clear
22
+ end
23
+
24
+ # Insert an item with an associated key into the SplayTreeMap, and returns the item inserted
25
+ #
26
+ # Complexity: amortized O(log n)
27
+ #
28
+ # map = Containers::SplayTreeMap.new
29
+ # map.push("MA", "Massachusetts") #=> "Massachusetts"
30
+ # map.get("MA") #=> "Massachusetts"
31
+ def push(key, value)
32
+ if @root.nil?
33
+ @root = Node.new(key, value)
34
+ @size = 1
35
+ return value
36
+ end
37
+ splay(key)
38
+
39
+ cmp = (key <=> @root.key)
40
+ if cmp == 0
41
+ @root.value = value
42
+ return value
43
+ end
44
+ node = Node.new(key, value)
45
+ if cmp < 1
46
+ node.left = @root.left
47
+ node.right = @root
48
+ @root.left = nil
49
+ else
50
+ node.right = @root.right
51
+ node.left = @root
52
+ @root.right = nil
53
+ end
54
+ @root = node
55
+ @size += 1
56
+ value
57
+ end
58
+ alias_method :[]=, :push
59
+
60
+ # Return the number of items in the SplayTreeMap.
61
+ #
62
+ # map = Containers::SplayTreeMap.new
63
+ # map.push("MA", "Massachusetts")
64
+ # map.push("GA", "Georgia")
65
+ # map.size #=> 2
66
+ def size
67
+ @size
68
+ end
69
+
70
+ # Remove all elements from the SplayTreeMap
71
+ #
72
+ # Complexity: O(1)
73
+ #
74
+ def clear
75
+ @root = nil
76
+ @size = 0
77
+ @header = Node.new(nil, nil)
78
+ end
79
+
80
+ # Return the height of the tree structure in the SplayTreeMap.
81
+ #
82
+ # Complexity: O(log n)
83
+ #
84
+ # map = Containers::SplayTreeMap.new
85
+ # map.push("MA", "Massachusetts")
86
+ # map.push("GA", "Georgia")
87
+ # map.height #=> 2
88
+ def height
89
+ height_recursive(@root)
90
+ end
91
+
92
+ # Return true if key is found in the SplayTreeMap, false otherwise.
93
+ #
94
+ # Complexity: amortized O(log n)
95
+ #
96
+ # map = Containers::SplayTreeMap.new
97
+ # map["MA"] = "Massachusetts"
98
+ # map["GA"] = "Georgia"
99
+ # map.has_key?("GA") #=> true
100
+ # map.has_key?("DE") #=> false
101
+ def has_key?(key)
102
+ !get(key).nil?
103
+ end
104
+
105
+ # Return the item associated with the key, or nil if none found.
106
+ #
107
+ # Complexity: amortized O(log n)
108
+ #
109
+ # map = Containers::SplayTreeMap.new
110
+ # map.push("MA", "Massachusetts")
111
+ # map.push("GA", "Georgia")
112
+ # map.get("GA") #=> "Georgia"
113
+ def get(key)
114
+ return nil if @root.nil?
115
+
116
+ splay(key)
117
+ (@root.key <=> key) == 0 ? @root.value : nil
118
+ end
119
+ alias_method :[], :get
120
+
121
+ # Return the smallest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
122
+ #
123
+ # Complexity: amortized O(log n)
124
+ #
125
+ # map = Containers::SplayTreeMap.new
126
+ # map["MA"] = "Massachusetts"
127
+ # map["GA"] = "Georgia"
128
+ # map.min #=> ["GA", "Georgia"]
129
+ def min
130
+ return nil if @root.nil?
131
+ n = @root
132
+ while n.left
133
+ n = n.left
134
+ end
135
+ splay(n.key)
136
+ return [n.key, n.value]
137
+ end
138
+
139
+ # Return the largest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
140
+ #
141
+ # Complexity: amortized O(log n)
142
+ #
143
+ # map = Containers::SplayTreeMap.new
144
+ # map["MA"] = "Massachusetts"
145
+ # map["GA"] = "Georgia"
146
+ # map.max #=> ["MA", "Massachusetts"]
147
+ def max
148
+ return nil if @root.nil?
149
+ n = @root
150
+ while n.right
151
+ n = n.right
152
+ end
153
+ splay(n.key)
154
+ return [n.key, n.value]
155
+ end
156
+
157
+ # Deletes the item and key if it's found, and returns the item. Returns nil
158
+ # if key is not present.
159
+ #
160
+ # Complexity: amortized O(log n)
161
+ #
162
+ # map = Containers::SplayTreeMap.new
163
+ # map["MA"] = "Massachusetts"
164
+ # map["GA"] = "Georgia"
165
+ # map.delete("GA") #=> "Georgia"
166
+ # map.delete("DE") #=> nil
167
+ def delete(key)
168
+ return nil if @root.nil?
169
+ deleted = nil
170
+ splay(key)
171
+ if (key <=> @root.key) == 0 # The key exists
172
+ deleted = @root.value
173
+ if @root.left.nil?
174
+ @root = @root.right
175
+ else
176
+ x = @root.right
177
+ @root = @root.left
178
+ splay(key)
179
+ @root.right = x
180
+ end
181
+ end
182
+ deleted
183
+ end
184
+
185
+ # Iterates over the SplayTreeMap in ascending order. Uses an iterative, not recursive, approach.
186
+ def each
187
+ return nil unless @root
188
+ stack = Containers::Stack.new
189
+ cursor = @root
190
+ loop do
191
+ if cursor
192
+ stack.push(cursor)
193
+ cursor = cursor.left
194
+ else
195
+ unless stack.empty?
196
+ cursor = stack.pop
197
+ yield(cursor.key, cursor.value)
198
+ cursor = cursor.right
199
+ else
200
+ break
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ class Node # :nodoc: all
207
+ attr_accessor :key, :value, :left, :right
208
+ def initialize(key, value)
209
+ @key = key
210
+ @value = value
211
+ @left = nil
212
+ @right = nil
213
+ end
214
+
215
+ def size
216
+ self.num_nodes
217
+ end
218
+ end
219
+
220
+ # Moves a key to the root, updating the structure in each step.
221
+ def splay(key)
222
+ l, r = @header, @header
223
+ t = @root
224
+ @header.left, @header.right = nil, nil
225
+
226
+ loop do
227
+ if (key <=> t.key) == -1
228
+ break unless t.left
229
+ if (key <=> t.left.key) == -1
230
+ y = t.left
231
+ t.left = y.right
232
+ y.right = t
233
+ t = y
234
+ break unless t.left
235
+ end
236
+ r.left = t
237
+ r = t
238
+ t = t.left
239
+ elsif (key <=> t.key) == 1
240
+ break unless t.right
241
+ if (key <=> t.right.key) == 1
242
+ y = t.right
243
+ t.right = y.left
244
+ y.left = t
245
+ t = y
246
+ break unless t.right
247
+ end
248
+ l.right = t
249
+ l = t
250
+ t = t.right
251
+ else
252
+ break
253
+ end
254
+ end
255
+ l.right = t.left
256
+ r.left = t.right
257
+ t.left = @header.right
258
+ t.right = @header.left
259
+ @root = t
260
+ end
261
+ private :splay
262
+
263
+ # Recursively determine height
264
+ def height_recursive(node)
265
+ return 0 if node.nil?
266
+
267
+ left_height = 1 + height_recursive(node.left)
268
+ right_height = 1 + height_recursive(node.right)
269
+
270
+ left_height > right_height ? left_height : right_height
271
+ end
272
+ private :height_recursive
273
+
274
+ end
@@ -1,29 +1,67 @@
1
- module Containers
2
- # Use a Ruby array for storage
3
- class Stack
4
- def initialize(ary=[])
5
- @container = ary
6
- end
1
+ require 'containers/deque'
2
+ =begin rdoc
3
+ A Stack is a container that keeps elements in a last-in first-out (LIFO) order. There are many
4
+ uses for stacks, including prefix-infix-postfix conversion and backtracking problems.
5
+
6
+ This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
7
+
8
+ =end
9
+ class Containers::Stack
10
+ include Enumerable
11
+ # Create a new stack. Takes an optional array argument to initialize the stack.
12
+ #
13
+ # s = Containers::Stack.new([1, 2, 3])
14
+ # s.pop #=> 3
15
+ # s.pop #=> 2
16
+ def initialize(ary=[])
17
+ @container = Containers::Deque.new(ary)
18
+ end
7
19
 
8
- def push(object)
9
- @container.push(object)
10
- end
20
+ # Returns the next item from the stack but does not remove it.
21
+ #
22
+ # s = Containers::Stack.new([1, 2, 3])
23
+ # s.next #=> 3
24
+ # s.size #=> 3
25
+ def next
26
+ @container.back
27
+ end
11
28
 
12
- def peek
13
- @container.last
14
- end
29
+ # Adds an item to the stack.
30
+ #
31
+ # s = Containers::Stack.new([1])
32
+ # s.push(2)
33
+ # s.pop #=> 2
34
+ # s.pop #=> 1
35
+ def push(obj)
36
+ @container.push_back(obj)
37
+ end
38
+ alias_method :<<, :push
15
39
 
16
- def pop
17
- @container.pop
18
- end
40
+ # Removes the next item from the stack and returns it.
41
+ #
42
+ # s = Containers::Stack.new([1, 2, 3])
43
+ # s.pop #=> 3
44
+ # s.size #=> 2
45
+ def pop
46
+ @container.pop_back
47
+ end
19
48
 
20
- def size
21
- @container.size
22
- end
49
+ # Return the number of items in the stack.
50
+ #
51
+ # s = Containers::Stack.new([1, 2, 3])
52
+ # s.size #=> 3
53
+ def size
54
+ @container.size
55
+ end
23
56
 
24
- def empty?
25
- @container.empty?
26
- end
57
+ # Returns true if the stack is empty, false otherwise.
58
+ def empty?
59
+ @container.empty?
60
+ end
27
61
 
62
+ # Iterate over the Stack in LIFO order.
63
+ def each(&block)
64
+ @container.each_backward(&block)
28
65
  end
66
+
29
67
  end
@@ -0,0 +1,68 @@
1
+ =begin rdoc
2
+ A suffix array enables fast substring search of a given string. An array of all possible substrings
3
+ is constructed and stored, and a binary search is then done to find a desired substring among those
4
+ stored. While more storage (and thus memory) is needed to create the SuffixArray, the advantage is
5
+ that substrings can be found in O(m log n) time, where m is the length of the substring to search for
6
+ and n is the total number of substrings.
7
+
8
+ =end
9
+ class Containers::SuffixArray
10
+ # Creates a new SuffixArray with a given string. Object of any class implementing a #to_s method can
11
+ # be passed in, such as integers.
12
+ #
13
+ # Complexity: O(n^2 log n)
14
+ #
15
+ # s_array = Containers::SuffixArray("abracadabra")
16
+ # s_array["abra"] #=> true
17
+ #
18
+ # number = Containers::SuffixArray(1234567)
19
+ # number[1] #=> true
20
+ # number[13] #=> false
21
+ def initialize(string)
22
+ string = string.to_s
23
+ raise ArgumentError, "SuffixArray needs to be initialized with a non-empty string" if string.empty?
24
+ @original_string = string
25
+ @suffixes = []
26
+ string.length.times do |i|
27
+ @suffixes << string[i..-1]
28
+ end
29
+
30
+ # Sort the suffixes in ascending order
31
+ @suffixes.sort! { |x, y| x <=> y }
32
+ end
33
+
34
+ # Returns true if the substring occurs in the string, false otherwise.
35
+ #
36
+ # Complexity: O(m + log n)
37
+ #
38
+ # s_array = Containers::SuffixArray.new("abracadabra")
39
+ # s_array.has_substring?("a") #=> true
40
+ # s_array.has_substring?("abra") #=> true
41
+ # s_array.has_substring?("abracadabra") #=> true
42
+ # s_array.has_substring?("acadabra") #=> true
43
+ # s_array.has_substring?("adabra") #=> true
44
+ # s_array.has_substring?("bra") #=> true
45
+ # s_array.has_substring?("bracadabra") #=> true
46
+ # s_array.has_substring?("cadabra") #=> true
47
+ # s_array.has_substring?("dabra") #=> true
48
+ # s_array.has_substring?("ra") #=> true
49
+ # s_array.has_substring?("racadabra") #=> true
50
+ # s_array.has_substring?("nope") #=> false
51
+ def has_substring?(substring)
52
+ substring = substring.to_s
53
+ return false if substring.empty?
54
+ substring_length = substring.length-1
55
+ l, r = 0, @suffixes.size-1
56
+ while(l <= r)
57
+ mid = (l + r) / 2
58
+ suffix = @suffixes[mid][0..substring_length]
59
+ case substring <=> suffix
60
+ when 0 then return true
61
+ when 1 then l = mid + 1
62
+ when -1 then r = mid - 1
63
+ end
64
+ end
65
+ return false
66
+ end
67
+ alias_method :[], :has_substring?
68
+ end
@@ -0,0 +1,182 @@
1
+ =begin rdoc
2
+ A Trie is a data structure that stores key value pairs in a tree-like fashion. It allows
3
+ O(m) lookup speed, where m is the length of the key searched, and has no chance of collisions,
4
+ unlike hash tables. Because of its nature, search misses are quickly detected.
5
+
6
+ Tries are often used for longest prefix algorithms, wildcard matching, and can be used to
7
+ implement a radix sort.
8
+
9
+ This implemention is based on a Ternary Search Tree.
10
+ =end
11
+ class Containers::Trie
12
+ # Create a new, empty Trie.
13
+ #
14
+ # t = Containers::Trie.new
15
+ # t["hello"] = "world"
16
+ # t["hello] #=> "world"
17
+ def initialize
18
+ @root = nil
19
+ end
20
+
21
+ # Adds a key, value pair to the Trie, and returns the value if successful. The to_s method is
22
+ # called on the parameter to turn it into a string.
23
+ #
24
+ # Complexity: O(m)
25
+ #
26
+ # t = Containers::Trie.new
27
+ # t["hello"] = "world"
28
+ # t.push("hello", "world") # does the same thing
29
+ # t["hello"] #=> "world"
30
+ # t[1] = 1
31
+ # t[1] #=> 1
32
+ def push(key, value)
33
+ key = key.to_s
34
+ return nil if key.empty?
35
+ @root = push_recursive(@root, key, 0, value)
36
+ value
37
+ end
38
+ alias_method :[]=, :push
39
+
40
+ # Returns true if the key is contained in the Trie.
41
+ #
42
+ # Complexity: O(m) worst case
43
+ #
44
+ def has_key?(key)
45
+ key = key.to_s
46
+ return false if key.empty?
47
+ !(get_recursive(@root, key, 0).nil?)
48
+ end
49
+
50
+ # Returns the value of the desired key, or nil if the key doesn't exist.
51
+ #
52
+ # Complexity: O(m) worst case
53
+ #
54
+ # t = Containers::Trie.new
55
+ # t.get("hello") = "world"
56
+ # t.get("non-existant") #=> nil
57
+ def get(key)
58
+ key = key.to_s
59
+ return nil if key.empty?
60
+ node = get_recursive(@root, key, 0)
61
+ node ? node.last : nil
62
+ end
63
+ alias_method :[], :get
64
+
65
+ # Returns the longest key that has a prefix in common with the parameter string. If
66
+ # no match is found, the blank string "" is returned.
67
+ #
68
+ # Complexity: O(m) worst case
69
+ #
70
+ # t = Containers::Trie.new
71
+ # t.push("Hello", "World")
72
+ # t.push("Hello, brother", "World")
73
+ # t.push("Hello, bob", "World")
74
+ # t.longest_prefix("Hello, brandon") #=> "Hello"
75
+ # t.longest_prefix("Hel") #=> ""
76
+ # t.longest_prefix("Hello") #=> "Hello"
77
+ def longest_prefix(string)
78
+ string = string.to_s
79
+ return nil if string.empty?
80
+ len = prefix_recursive(@root, string, 0)
81
+ string[0...len]
82
+ end
83
+
84
+ # Returns a sorted array containing strings that match the parameter string. The wildcard
85
+ # characters that match any character are '*' and '.' If no match is found, an empty
86
+ # array is returned.
87
+ #
88
+ # Complexity: O(n) worst case
89
+ #
90
+ # t = Containers::Trie.new
91
+ # t.push("Hello", "World")
92
+ # t.push("Hilly", "World")
93
+ # t.push("Hello, bob", "World")
94
+ # t.wildcard("H*ll.") #=> ["Hello", "Hilly"]
95
+ # t.wildcard("Hel") #=> []
96
+ def wildcard(string)
97
+ string = string.to_s
98
+ return nil if string.empty?
99
+ ary = []
100
+ ary << wildcard_recursive(@root, string, 0, "")
101
+ ary.flatten.compact.sort
102
+ end
103
+
104
+ class Node # :nodoc: all
105
+ attr_accessor :left, :mid, :right, :char, :value, :end
106
+
107
+ def initialize(char, value)
108
+ @char = char
109
+ @value = value
110
+ @left = @mid = @right = nil
111
+ @end = false
112
+ end
113
+
114
+ def last?
115
+ @end == true
116
+ end
117
+ end
118
+
119
+ def wildcard_recursive(node, string, index, prefix)
120
+ return nil if node.nil? || index == string.length
121
+ arr = []
122
+ char = string[index]
123
+ if (char.chr == "*" || char.chr == "." || char < node.char)
124
+ arr << wildcard_recursive(node.left, string, index, prefix)
125
+ end
126
+ if (char.chr == "*" || char.chr == "." || char > node.char)
127
+ arr << wildcard_recursive(node.right, string, index, prefix)
128
+ end
129
+ if (char.chr == "*" || char.chr == "." || char == node.char)
130
+ arr << "#{prefix}#{node.char.chr}" if node.last?
131
+ arr << wildcard_recursive(node.mid, string, index+1, prefix + node.char.chr)
132
+ end
133
+ arr
134
+ end
135
+
136
+ def prefix_recursive(node, string, index)
137
+ return 0 if node.nil? || index == string.length
138
+ len = 0
139
+ rec_len = 0
140
+ char = string[index]
141
+ if (char < node.char)
142
+ rec_len = prefix_recursive(node.left, string, index)
143
+ elsif (char > node.char)
144
+ rec_len = prefix_recursive(node.right, string, index)
145
+ else
146
+ len = index+1 if node.last?
147
+ rec_len = prefix_recursive(node.mid, string, index+1)
148
+ end
149
+ len > rec_len ? len : rec_len
150
+ end
151
+
152
+ def push_recursive(node, string, index, value)
153
+ char = string[index]
154
+ node = Node.new(char, value) if node.nil?
155
+ if (char < node.char)
156
+ node.left = push_recursive(node.left, string, index, value)
157
+ elsif (char > node.char)
158
+ node.right = push_recursive(node.right, string, index, value)
159
+ elsif (index < string.length-1) # We're not at the end of the input string; add next char
160
+ node.mid = push_recursive(node.mid, string, index+1, value)
161
+ else
162
+ node.end = true
163
+ node.value = value
164
+ end
165
+ node
166
+ end
167
+
168
+ # Returns [char, value] if found
169
+ def get_recursive(node, string, index)
170
+ return nil if node.nil?
171
+ char = string[index]
172
+ if (char < node.char)
173
+ return get_recursive(node.left, string, index)
174
+ elsif (char > node.char)
175
+ return get_recursive(node.right, string, index)
176
+ elsif (index < string.length-1) # We're not at the end of the input string; add next char
177
+ return get_recursive(node.mid, string, index+1)
178
+ else
179
+ return node.last? ? [node.char, node.value] : nil
180
+ end
181
+ end
182
+ end