algorithms 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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