amorim-algorithms 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.markdown +193 -0
  3. data/Gemfile +9 -0
  4. data/Manifest +51 -0
  5. data/README.markdown +87 -0
  6. data/Rakefile +22 -0
  7. data/algorithms.gemspec +23 -0
  8. data/benchmarks/deque.rb +17 -0
  9. data/benchmarks/sorts.rb +34 -0
  10. data/benchmarks/treemaps.rb +51 -0
  11. data/ext/algorithms/string/extconf.rb +4 -0
  12. data/ext/algorithms/string/string.c +68 -0
  13. data/ext/containers/bst/bst.c +247 -0
  14. data/ext/containers/bst/extconf.rb +4 -0
  15. data/ext/containers/deque/deque.c +247 -0
  16. data/ext/containers/deque/extconf.rb +4 -0
  17. data/ext/containers/rbtree_map/extconf.rb +4 -0
  18. data/ext/containers/rbtree_map/rbtree.c +498 -0
  19. data/ext/containers/splaytree_map/extconf.rb +4 -0
  20. data/ext/containers/splaytree_map/splaytree.c +419 -0
  21. data/lib/algorithms.rb +66 -0
  22. data/lib/algorithms/search.rb +84 -0
  23. data/lib/algorithms/sort.rb +368 -0
  24. data/lib/algorithms/string.rb +9 -0
  25. data/lib/containers/deque.rb +171 -0
  26. data/lib/containers/heap.rb +499 -0
  27. data/lib/containers/kd_tree.rb +110 -0
  28. data/lib/containers/priority_queue.rb +113 -0
  29. data/lib/containers/queue.rb +68 -0
  30. data/lib/containers/rb_tree_map.rb +398 -0
  31. data/lib/containers/splay_tree_map.rb +269 -0
  32. data/lib/containers/stack.rb +67 -0
  33. data/lib/containers/suffix_array.rb +68 -0
  34. data/lib/containers/trie.rb +182 -0
  35. data/spec/bst_gc_mark_spec.rb +25 -0
  36. data/spec/bst_spec.rb +25 -0
  37. data/spec/deque_gc_mark_spec.rb +18 -0
  38. data/spec/deque_spec.rb +108 -0
  39. data/spec/heap_spec.rb +131 -0
  40. data/spec/kd_expected_out.txt +10000 -0
  41. data/spec/kd_test_in.txt +10000 -0
  42. data/spec/kd_tree_spec.rb +34 -0
  43. data/spec/map_gc_mark_spec.rb +29 -0
  44. data/spec/priority_queue_spec.rb +75 -0
  45. data/spec/queue_spec.rb +61 -0
  46. data/spec/rb_tree_map_spec.rb +123 -0
  47. data/spec/search_spec.rb +28 -0
  48. data/spec/sort_spec.rb +29 -0
  49. data/spec/splay_tree_map_spec.rb +106 -0
  50. data/spec/stack_spec.rb +60 -0
  51. data/spec/string_spec.rb +15 -0
  52. data/spec/suffix_array_spec.rb +40 -0
  53. data/spec/trie_spec.rb +59 -0
  54. metadata +108 -0
@@ -0,0 +1,269 @@
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::RubySplayTreeMap
17
+ include Enumerable
18
+
19
+ Node = Struct.new(:key, :value, :left, :right)
20
+
21
+ # Create and initialize a new empty SplayTreeMap.
22
+ def initialize
23
+ @size = 0
24
+ clear
25
+ end
26
+
27
+ # Insert an item with an associated key into the SplayTreeMap, and returns the item inserted
28
+ #
29
+ # Complexity: amortized O(log n)
30
+ #
31
+ # map = Containers::SplayTreeMap.new
32
+ # map.push("MA", "Massachusetts") #=> "Massachusetts"
33
+ # map.get("MA") #=> "Massachusetts"
34
+ def push(key, value)
35
+ if @root.nil?
36
+ @root = Node.new(key, value, nil, nil)
37
+ @size = 1
38
+ return value
39
+ end
40
+ splay(key)
41
+
42
+ cmp = (key <=> @root.key)
43
+ if cmp == 0
44
+ @root.value = value
45
+ return value
46
+ end
47
+ node = Node.new(key, value, nil, nil)
48
+ if cmp < 1
49
+ node.left = @root.left
50
+ node.right = @root
51
+ @root.left = nil
52
+ else
53
+ node.right = @root.right
54
+ node.left = @root
55
+ @root.right = nil
56
+ end
57
+ @root = node
58
+ @size += 1
59
+ value
60
+ end
61
+ alias_method :[]=, :push
62
+
63
+ # Return the number of items in the SplayTreeMap.
64
+ #
65
+ # map = Containers::SplayTreeMap.new
66
+ # map.push("MA", "Massachusetts")
67
+ # map.push("GA", "Georgia")
68
+ # map.size #=> 2
69
+ def size
70
+ @size
71
+ end
72
+
73
+ # Remove all elements from the SplayTreeMap
74
+ #
75
+ # Complexity: O(1)
76
+ #
77
+ def clear
78
+ @root = nil
79
+ @size = 0
80
+ @header = Node.new(nil, nil, nil, nil)
81
+ end
82
+
83
+ # Return the height of the tree structure in the SplayTreeMap.
84
+ #
85
+ # Complexity: O(log n)
86
+ #
87
+ # map = Containers::SplayTreeMap.new
88
+ # map.push("MA", "Massachusetts")
89
+ # map.push("GA", "Georgia")
90
+ # map.height #=> 2
91
+ def height
92
+ height_recursive(@root)
93
+ end
94
+
95
+ # Return true if key is found in the SplayTreeMap, false otherwise.
96
+ #
97
+ # Complexity: amortized O(log n)
98
+ #
99
+ # map = Containers::SplayTreeMap.new
100
+ # map["MA"] = "Massachusetts"
101
+ # map["GA"] = "Georgia"
102
+ # map.has_key?("GA") #=> true
103
+ # map.has_key?("DE") #=> false
104
+ def has_key?(key)
105
+ !get(key).nil?
106
+ end
107
+
108
+ # Return the item associated with the key, or nil if none found.
109
+ #
110
+ # Complexity: amortized O(log n)
111
+ #
112
+ # map = Containers::SplayTreeMap.new
113
+ # map.push("MA", "Massachusetts")
114
+ # map.push("GA", "Georgia")
115
+ # map.get("GA") #=> "Georgia"
116
+ def get(key)
117
+ return nil if @root.nil?
118
+
119
+ splay(key)
120
+ (@root.key <=> key) == 0 ? @root.value : nil
121
+ end
122
+ alias_method :[], :get
123
+
124
+ # Return the smallest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
125
+ #
126
+ # Complexity: amortized O(log n)
127
+ #
128
+ # map = Containers::SplayTreeMap.new
129
+ # map["MA"] = "Massachusetts"
130
+ # map["GA"] = "Georgia"
131
+ # map.min #=> ["GA", "Georgia"]
132
+ def min
133
+ return nil if @root.nil?
134
+ n = @root
135
+ while n.left
136
+ n = n.left
137
+ end
138
+ splay(n.key)
139
+ return [n.key, n.value]
140
+ end
141
+
142
+ # Return the largest [key, value] pair in the SplayTreeMap, or nil if the tree is empty.
143
+ #
144
+ # Complexity: amortized O(log n)
145
+ #
146
+ # map = Containers::SplayTreeMap.new
147
+ # map["MA"] = "Massachusetts"
148
+ # map["GA"] = "Georgia"
149
+ # map.max #=> ["MA", "Massachusetts"]
150
+ def max
151
+ return nil if @root.nil?
152
+ n = @root
153
+ while n.right
154
+ n = n.right
155
+ end
156
+ splay(n.key)
157
+ return [n.key, n.value]
158
+ end
159
+
160
+ # Deletes the item and key if it's found, and returns the item. Returns nil
161
+ # if key is not present.
162
+ #
163
+ # Complexity: amortized O(log n)
164
+ #
165
+ # map = Containers::SplayTreeMap.new
166
+ # map["MA"] = "Massachusetts"
167
+ # map["GA"] = "Georgia"
168
+ # map.delete("GA") #=> "Georgia"
169
+ # map.delete("DE") #=> nil
170
+ def delete(key)
171
+ return nil if @root.nil?
172
+ deleted = nil
173
+ splay(key)
174
+ if (key <=> @root.key) == 0 # The key exists
175
+ deleted = @root.value
176
+ if @root.left.nil?
177
+ @root = @root.right
178
+ else
179
+ x = @root.right
180
+ @root = @root.left
181
+ splay(key)
182
+ @root.right = x
183
+ end
184
+ end
185
+ deleted
186
+ end
187
+
188
+ # Iterates over the SplayTreeMap in ascending order. Uses an iterative, not recursive, approach.
189
+ def each
190
+ return nil unless @root
191
+ stack = Containers::Stack.new
192
+ cursor = @root
193
+ loop do
194
+ if cursor
195
+ stack.push(cursor)
196
+ cursor = cursor.left
197
+ else
198
+ unless stack.empty?
199
+ cursor = stack.pop
200
+ yield(cursor.key, cursor.value)
201
+ cursor = cursor.right
202
+ else
203
+ break
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ # Moves a key to the root, updating the structure in each step.
210
+ def splay(key)
211
+ l, r = @header, @header
212
+ t = @root
213
+ @header.left, @header.right = nil, nil
214
+
215
+ loop do
216
+ if (key <=> t.key) == -1
217
+ break unless t.left
218
+ if (key <=> t.left.key) == -1
219
+ y = t.left
220
+ t.left = y.right
221
+ y.right = t
222
+ t = y
223
+ break unless t.left
224
+ end
225
+ r.left = t
226
+ r = t
227
+ t = t.left
228
+ elsif (key <=> t.key) == 1
229
+ break unless t.right
230
+ if (key <=> t.right.key) == 1
231
+ y = t.right
232
+ t.right = y.left
233
+ y.left = t
234
+ t = y
235
+ break unless t.right
236
+ end
237
+ l.right = t
238
+ l = t
239
+ t = t.right
240
+ else
241
+ break
242
+ end
243
+ end
244
+ l.right = t.left
245
+ r.left = t.right
246
+ t.left = @header.right
247
+ t.right = @header.left
248
+ @root = t
249
+ end
250
+ private :splay
251
+
252
+ # Recursively determine height
253
+ def height_recursive(node)
254
+ return 0 if node.nil?
255
+
256
+ left_height = 1 + height_recursive(node.left)
257
+ right_height = 1 + height_recursive(node.right)
258
+
259
+ left_height > right_height ? left_height : right_height
260
+ end
261
+ private :height_recursive
262
+ end
263
+
264
+ begin
265
+ require 'CSplayTreeMap'
266
+ Containers::SplayTreeMap = Containers::CSplayTreeMap
267
+ rescue LoadError # C Version could not be found, try ruby version
268
+ Containers::SplayTreeMap = Containers::RubySplayTreeMap
269
+ end
@@ -0,0 +1,67 @@
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
19
+
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
28
+
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
39
+
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
48
+
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
56
+
57
+ # Returns true if the stack is empty, false otherwise.
58
+ def empty?
59
+ @container.empty?
60
+ end
61
+
62
+ # Iterate over the Stack in LIFO order.
63
+ def each(&block)
64
+ @container.each_backward(&block)
65
+ end
66
+
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