victory 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +11 -1
- data/README.md +4 -6
- data/Rakefile +11 -4
- data/USAGE.md +159 -0
- data/ext/algorithms/string/LICENSE.md +21 -0
- data/ext/algorithms/string/extconf.rb +4 -0
- data/ext/algorithms/string/string.c +68 -0
- data/ext/containers/bst/LICENSE.md +21 -0
- data/ext/containers/bst/bst.c +247 -0
- data/ext/containers/bst/extconf.rb +4 -0
- data/ext/containers/deque/LICENSE.md +21 -0
- data/ext/containers/deque/deque.c +247 -0
- data/ext/containers/deque/extconf.rb +4 -0
- data/ext/containers/rbtree_map/LICENSE.md +21 -0
- data/ext/containers/rbtree_map/extconf.rb +4 -0
- data/ext/containers/rbtree_map/rbtree.c +498 -0
- data/ext/containers/splaytree_map/LICENSE.md +21 -0
- data/ext/containers/splaytree_map/extconf.rb +4 -0
- data/ext/containers/splaytree_map/splaytree.c +419 -0
- data/ext/containers/xor_list/extconf.rb +4 -0
- data/ext/containers/xor_list/xor_list.c +122 -0
- data/lib/algorithms/search.rb +104 -0
- data/lib/algorithms/sort.rb +389 -0
- data/lib/algorithms/string.rb +29 -0
- data/lib/containers/deque.rb +193 -0
- data/lib/containers/heap.rb +524 -0
- data/lib/containers/kd_tree.rb +131 -0
- data/lib/containers/list.rb +81 -0
- data/lib/containers/prefix_tree.rb +61 -0
- data/lib/containers/priority_queue.rb +135 -0
- data/lib/containers/queue.rb +89 -0
- data/lib/containers/rb_tree_map.rb +420 -0
- data/lib/containers/splay_tree_map.rb +290 -0
- data/lib/containers/stack.rb +88 -0
- data/lib/containers/suffix_array.rb +92 -0
- data/lib/containers/trie.rb +204 -0
- data/lib/containers/tuple.rb +20 -0
- data/lib/victory/version.rb +1 -1
- data/lib/victory.rb +8 -1
- data/victory.gemspec +12 -3
- metadata +73 -12
- data/.idea/encodings.xml +0 -4
- data/.idea/misc.xml +0 -7
- data/.idea/modules.xml +0 -8
- data/.idea/victory.iml +0 -13
- data/.idea/workspace.xml +0 -233
- data/ext/victory/extconf.rb +0 -3
- data/ext/victory/victory.c +0 -9
- data/ext/victory/victory.h +0 -6
@@ -0,0 +1,389 @@
|
|
1
|
+
require 'containers/heap' # for heapsort
|
2
|
+
|
3
|
+
# rdoc
|
4
|
+
# This module implements sorting algorithms. Documentation is provided for each algorithm.
|
5
|
+
#
|
6
|
+
# MIT License
|
7
|
+
#
|
8
|
+
# Copyright (c) 2009 Kanwei Li
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
# of this software and associated documentation files (the "Software"), to deal
|
12
|
+
# in the Software without restriction, including without limitation the rights
|
13
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
# copies of the Software, and to permit persons to whom the Software is
|
15
|
+
# furnished to do so, subject to the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be included in all
|
18
|
+
# copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
# SOFTWARE.
|
27
|
+
|
28
|
+
module Algorithms::Sort
|
29
|
+
# Bubble sort: A very naive sort that keeps swapping elements until the container is sorted.
|
30
|
+
# Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
|
31
|
+
# be implemented for the container.
|
32
|
+
# Time Complexity: О(n^2)
|
33
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
34
|
+
# Stable: Yes
|
35
|
+
#
|
36
|
+
# Algorithms::Sort.bubble_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
37
|
+
def self.bubble_sort(container)
|
38
|
+
loop do
|
39
|
+
swapped = false
|
40
|
+
(container.size-1).times do |i|
|
41
|
+
if (container[i] <=> container[i+1]) == 1
|
42
|
+
container[i], container[i+1] = container[i+1], container[i] # Swap
|
43
|
+
swapped = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
break unless swapped
|
47
|
+
end
|
48
|
+
container
|
49
|
+
end
|
50
|
+
|
51
|
+
# Comb sort: A variation on bubble sort that dramatically improves performance.
|
52
|
+
# Source: http://yagni.com/combsort/
|
53
|
+
# Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
|
54
|
+
# be implemented for the container.
|
55
|
+
# Time Complexity: О(n^2)
|
56
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
57
|
+
# Stable: Yes
|
58
|
+
#
|
59
|
+
# Algorithms::Sort.comb_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
60
|
+
def self.comb_sort(container)
|
61
|
+
container
|
62
|
+
gap = container.size
|
63
|
+
loop do
|
64
|
+
gap = gap * 10/13
|
65
|
+
gap = 11 if gap == 9 || gap == 10
|
66
|
+
gap = 1 if gap < 1
|
67
|
+
swapped = false
|
68
|
+
(container.size - gap).times do |i|
|
69
|
+
if (container[i] <=> container[i + gap]) == 1
|
70
|
+
container[i], container[i+gap] = container[i+gap], container[i] # Swap
|
71
|
+
swapped = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
break if !swapped && gap == 1
|
75
|
+
end
|
76
|
+
container
|
77
|
+
end
|
78
|
+
|
79
|
+
# Selection sort: A naive sort that goes through the container and selects the smallest element,
|
80
|
+
# putting it at the beginning. Repeat until the end is reached.
|
81
|
+
# Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
|
82
|
+
# be implemented for the container.
|
83
|
+
# Time Complexity: О(n^2)
|
84
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
85
|
+
# Stable: Yes
|
86
|
+
#
|
87
|
+
# Algorithms::Sort.selection_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
88
|
+
def self.selection_sort(container)
|
89
|
+
0.upto(container.size-1) do |i|
|
90
|
+
min = i
|
91
|
+
(i+1).upto(container.size-1) do |j|
|
92
|
+
min = j if (container[j] <=> container[min]) == -1
|
93
|
+
end
|
94
|
+
container[i], container[min] = container[min], container[i] # Swap
|
95
|
+
end
|
96
|
+
container
|
97
|
+
end
|
98
|
+
|
99
|
+
# Heap sort: Uses a heap (implemented by the Containers module) to sort the collection.
|
100
|
+
# Requirements: Needs to be able to compare elements with <=>
|
101
|
+
# Time Complexity: О(n^2)
|
102
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
103
|
+
# Stable: Yes
|
104
|
+
#
|
105
|
+
# Algorithms::Sort.heapsort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
106
|
+
def self.heapsort(container)
|
107
|
+
heap = Containers::Heap.new(container)
|
108
|
+
ary = []
|
109
|
+
ary << heap.pop until heap.empty?
|
110
|
+
ary
|
111
|
+
end
|
112
|
+
|
113
|
+
# Insertion sort: Elements are inserted sequentially into the right position.
|
114
|
+
# Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
|
115
|
+
# be implemented for the container.
|
116
|
+
# Time Complexity: О(n^2)
|
117
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
118
|
+
# Stable: Yes
|
119
|
+
#
|
120
|
+
# Algorithms::Sort.insertion_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
121
|
+
def self.insertion_sort(container)
|
122
|
+
return container if container.size < 2
|
123
|
+
(1..container.size-1).each do |i|
|
124
|
+
value = container[i]
|
125
|
+
j = i-1
|
126
|
+
while j >= 0 and container[j] > value do
|
127
|
+
container[j+1] = container[j]
|
128
|
+
j = j-1
|
129
|
+
end
|
130
|
+
container[j+1] = value
|
131
|
+
end
|
132
|
+
container
|
133
|
+
end
|
134
|
+
|
135
|
+
# Shell sort: Similar approach as insertion sort but slightly better.
|
136
|
+
# Requirements: Needs to be able to compare elements with <=>, and the [] []= methods should
|
137
|
+
# be implemented for the container.
|
138
|
+
# Time Complexity: О(n^2)
|
139
|
+
# Space Complexity: О(n) total, O(1) auxiliary
|
140
|
+
# Stable: Yes
|
141
|
+
#
|
142
|
+
# Algorithms::Sort.shell_sort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
143
|
+
def self.shell_sort(container)
|
144
|
+
increment = container.size/2
|
145
|
+
while increment > 0 do
|
146
|
+
(increment..container.size-1).each do |i|
|
147
|
+
temp = container[i]
|
148
|
+
j = i
|
149
|
+
while j >= increment && container[j - increment] > temp do
|
150
|
+
container[j] = container[j-increment]
|
151
|
+
j -= increment
|
152
|
+
end
|
153
|
+
container[j] = temp
|
154
|
+
end
|
155
|
+
increment = (increment == 2 ? 1 : (increment / 2.2).round)
|
156
|
+
end
|
157
|
+
container
|
158
|
+
end
|
159
|
+
|
160
|
+
# Quicksort: A divide-and-conquer sort that recursively partitions a container until it is sorted.
|
161
|
+
# Requirements: Container should implement #pop and include the Enumerable module.
|
162
|
+
# Time Complexity: О(n log n) average, O(n^2) worst-case
|
163
|
+
# Space Complexity: О(n) auxiliary
|
164
|
+
# Stable: No
|
165
|
+
#
|
166
|
+
# Algorithms::Sort.quicksort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
167
|
+
# def self.quicksort(container)
|
168
|
+
# return [] if container.empty?
|
169
|
+
#
|
170
|
+
# x, *xs = container
|
171
|
+
#
|
172
|
+
# quicksort(xs.select { |i| i < x }) + [x] + quicksort(xs.select { |i| i >= x })
|
173
|
+
# end
|
174
|
+
|
175
|
+
def self.partition(data, left, right)
|
176
|
+
pivot = data[front]
|
177
|
+
left += 1
|
178
|
+
|
179
|
+
while left <= right do
|
180
|
+
if data[frontUnknown] < pivot
|
181
|
+
back += 1
|
182
|
+
data[frontUnknown], data[back] = data[back], data[frontUnknown] # Swap
|
183
|
+
end
|
184
|
+
|
185
|
+
frontUnknown += 1
|
186
|
+
end
|
187
|
+
|
188
|
+
data[front], data[back] = data[back], data[front] # Swap
|
189
|
+
back
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
# def self.quicksort(container, left = 0, right = container.size - 1)
|
194
|
+
# if left < right
|
195
|
+
# middle = partition(container, left, right)
|
196
|
+
# quicksort(container, left, middle - 1)
|
197
|
+
# quicksort(container, middle + 1, right)
|
198
|
+
# end
|
199
|
+
# end
|
200
|
+
|
201
|
+
def self.quicksort(container)
|
202
|
+
bottom, top = [], []
|
203
|
+
top[0] = 0
|
204
|
+
bottom[0] = container.size
|
205
|
+
i = 0
|
206
|
+
while i >= 0 do
|
207
|
+
l = top[i]
|
208
|
+
r = bottom[i] - 1;
|
209
|
+
if l < r
|
210
|
+
pivot = container[l]
|
211
|
+
while l < r do
|
212
|
+
r -= 1 while (container[r] >= pivot && l < r)
|
213
|
+
if (l < r)
|
214
|
+
container[l] = container[r]
|
215
|
+
l += 1
|
216
|
+
end
|
217
|
+
l += 1 while (container[l] <= pivot && l < r)
|
218
|
+
if (l < r)
|
219
|
+
container[r] = container[l]
|
220
|
+
r -= 1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
container[l] = pivot
|
224
|
+
top[i+1] = l + 1
|
225
|
+
bottom[i+1] = bottom[i]
|
226
|
+
bottom[i] = l
|
227
|
+
i += 1
|
228
|
+
else
|
229
|
+
i -= 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
container
|
233
|
+
end
|
234
|
+
|
235
|
+
# Mergesort: A stable divide-and-conquer sort that sorts small chunks of the container and then merges them together.
|
236
|
+
# Returns an array of the sorted elements.
|
237
|
+
# Requirements: Container should implement []
|
238
|
+
# Time Complexity: О(n log n) average and worst-case
|
239
|
+
# Space Complexity: О(n) auxiliary
|
240
|
+
# Stable: Yes
|
241
|
+
#
|
242
|
+
# Algorithms::Sort.mergesort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
243
|
+
def self.mergesort(container)
|
244
|
+
return container if container.size <= 1
|
245
|
+
mid = container.size / 2
|
246
|
+
left = container[0...mid]
|
247
|
+
right = container[mid...container.size]
|
248
|
+
merge(mergesort(left), mergesort(right))
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.merge(left, right)
|
252
|
+
sorted = []
|
253
|
+
until left.empty? or right.empty?
|
254
|
+
left.first <= right.first ? sorted << left.shift : sorted << right.shift
|
255
|
+
end
|
256
|
+
sorted + left + right
|
257
|
+
end
|
258
|
+
|
259
|
+
# Dual-Pivot Quicksort is a variation of Quicksort by Vladimir Yaroslavskiy.
|
260
|
+
# This is an implementation of the algorithm as it was found in the original
|
261
|
+
# research paper:
|
262
|
+
#
|
263
|
+
# http://iaroslavski.narod.ru/quicksort/DualPivotQuicksort.pdf
|
264
|
+
#
|
265
|
+
# Mirror:
|
266
|
+
# http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
|
267
|
+
#
|
268
|
+
# "This algorithm offers O(n log(n)) performance on many data sets that cause
|
269
|
+
# other quicksorts to degrade to quadratic performance, and is typically
|
270
|
+
# faster than traditional (one-pivot) Quicksort implementations."
|
271
|
+
# -- http://download.oracle.com/javase/7/docs/api/java/util/Arrays.html
|
272
|
+
#
|
273
|
+
# The algorithm was improved by Vladimir Yaroslavskiy, Jon Bentley, and
|
274
|
+
# Joshua Bloch, and was implemented as the default sort algorithm for
|
275
|
+
# primatives in Java 7.
|
276
|
+
#
|
277
|
+
# Implementation in the Java JDK as of November, 2011:
|
278
|
+
# http://www.docjar.com/html/api/java/util/DualPivotQuicksort.java.html
|
279
|
+
#
|
280
|
+
# It is proved that for the Dual-Pivot Quicksort the average number
|
281
|
+
# of comparisons is 2*n*ln(n), the average number of swaps is
|
282
|
+
# 0.8*n*ln(n), whereas classical Quicksort algorithm has 2*n*ln(n)
|
283
|
+
# and 1*n*ln(n) respectively. This has been fully examined mathematically
|
284
|
+
# and experimentally.
|
285
|
+
#
|
286
|
+
# Requirements: Container should implement #pop and include the Enumerable module.
|
287
|
+
# Time Complexity: О(n log n) average, О(n log n) worst-case
|
288
|
+
# Space Complexity: О(n) auxiliary
|
289
|
+
#
|
290
|
+
# Stable: No
|
291
|
+
#
|
292
|
+
# Algorithms::Sort.dualpivotquicksort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
293
|
+
|
294
|
+
def self.dualpivotquicksort(container)
|
295
|
+
return container if container.size <= 1
|
296
|
+
dualpivot(container, 0, container.size-1, 3)
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.dualpivot(container, left=0, right=container.size-1, div=3)
|
300
|
+
length = right - left
|
301
|
+
if length < 27 # insertion sort for tiny array
|
302
|
+
container.each_with_index do |data,i|
|
303
|
+
j = i - 1
|
304
|
+
while j >= 0
|
305
|
+
break if container[j] <= data
|
306
|
+
container[j + 1] = container[j]
|
307
|
+
j = j - 1
|
308
|
+
end
|
309
|
+
container[j + 1] = data
|
310
|
+
end
|
311
|
+
else # full dual-pivot quicksort
|
312
|
+
third = length / div
|
313
|
+
# medians
|
314
|
+
m1 = left + third
|
315
|
+
m2 = right - third
|
316
|
+
if m1 <= left
|
317
|
+
m1 = left + 1
|
318
|
+
end
|
319
|
+
if m2 >= right
|
320
|
+
m2 = right - 1
|
321
|
+
end
|
322
|
+
if container[m1] < container[m2]
|
323
|
+
dualpivot_swap(container, m1, left)
|
324
|
+
dualpivot_swap(container, m2, right)
|
325
|
+
else
|
326
|
+
dualpivot_swap(container, m1, right)
|
327
|
+
dualpivot_swap(container, m2, left)
|
328
|
+
end
|
329
|
+
# pivots
|
330
|
+
pivot1 = container[left]
|
331
|
+
pivot2 = container[right]
|
332
|
+
# pointers
|
333
|
+
less = left + 1
|
334
|
+
great = right -1
|
335
|
+
# sorting
|
336
|
+
k = less
|
337
|
+
while k <= great
|
338
|
+
if container[k] < pivot1
|
339
|
+
dualpivot_swap(container, k, less += 1)
|
340
|
+
elsif container[k] > pivot2
|
341
|
+
while k < great && container[great] > pivot2
|
342
|
+
great -= 1
|
343
|
+
end
|
344
|
+
dualpivot_swap(container, k, great -= 1)
|
345
|
+
if container[k] < pivot1
|
346
|
+
dualpivot_swap(container, k, less += 1)
|
347
|
+
end
|
348
|
+
end
|
349
|
+
k += 1
|
350
|
+
end
|
351
|
+
# swaps
|
352
|
+
dist = great - less
|
353
|
+
if dist < 13
|
354
|
+
div += 1
|
355
|
+
end
|
356
|
+
dualpivot_swap(container, less-1, left)
|
357
|
+
dualpivot_swap(container, great+1, right)
|
358
|
+
# subarrays
|
359
|
+
dualpivot(container, left, less-2, div)
|
360
|
+
dualpivot(container, great+2, right, div)
|
361
|
+
# equal elements
|
362
|
+
if dist > length - 13 && pivot1 != pivot2
|
363
|
+
for k in less..great do
|
364
|
+
if container[k] == pivot1
|
365
|
+
dualpivot_swap(container, k, less)
|
366
|
+
less += 1
|
367
|
+
elsif container[k] == pivot2
|
368
|
+
dualpivot_swap(container, k, great)
|
369
|
+
great -= 1
|
370
|
+
if container[k] == pivot1
|
371
|
+
dualpivot_swap(container, k, less)
|
372
|
+
less += 1
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
end
|
377
|
+
# subarray
|
378
|
+
if pivot1 < pivot2
|
379
|
+
dualpivot(container, less, great, div)
|
380
|
+
end
|
381
|
+
container
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def self.dualpivot_swap(container, i, j)
|
386
|
+
container[i], container[j] = container[j], container[i]
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# rdoc
|
2
|
+
# This module implements string algorithms. Documentation is provided for each algorithm.
|
3
|
+
#
|
4
|
+
# MIT License
|
5
|
+
#
|
6
|
+
# Copyright (c) 2009 Kanwei Li
|
7
|
+
#
|
8
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
9
|
+
# of this software and associated documentation files (the "Software"), to deal
|
10
|
+
# in the Software without restriction, including without limitation the rights
|
11
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
12
|
+
# copies of the Software, and to permit persons to whom the Software is
|
13
|
+
# furnished to do so, subject to the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be included in all
|
16
|
+
# copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
19
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
20
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
21
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
22
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
23
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
24
|
+
# SOFTWARE.
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'CString'
|
28
|
+
rescue LoadError
|
29
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# rdoc
|
2
|
+
# A Deque is a container that allows items to be added and removed from both the front and back,
|
3
|
+
# acting as a combination of a Stack and Queue.
|
4
|
+
#
|
5
|
+
# This implementation uses a doubly-linked list, guaranteeing O(1) complexity for all operations.
|
6
|
+
#
|
7
|
+
#
|
8
|
+
# MIT License
|
9
|
+
#
|
10
|
+
# Copyright (c) 2009 Kanwei Li
|
11
|
+
#
|
12
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
13
|
+
# of this software and associated documentation files (the "Software"), to deal
|
14
|
+
# in the Software without restriction, including without limitation the rights
|
15
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
16
|
+
# copies of the Software, and to permit persons to whom the Software is
|
17
|
+
# furnished to do so, subject to the following conditions:
|
18
|
+
#
|
19
|
+
# The above copyright notice and this permission notice shall be included in all
|
20
|
+
# copies or substantial portions of the Software.
|
21
|
+
#
|
22
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
23
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
24
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
25
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
26
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
27
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
28
|
+
# SOFTWARE.
|
29
|
+
class Containers::RubyDeque
|
30
|
+
include Enumerable
|
31
|
+
|
32
|
+
Node = Struct.new(:left, :right, :obj)
|
33
|
+
|
34
|
+
# Create a new Deque. Takes an optional array argument to initialize the Deque.
|
35
|
+
#
|
36
|
+
# d = Containers::Deque.new([1, 2, 3])
|
37
|
+
# d.front #=> 1
|
38
|
+
# d.back #=> 3
|
39
|
+
def initialize(ary=[])
|
40
|
+
@front = nil
|
41
|
+
@back = nil
|
42
|
+
@size = 0
|
43
|
+
ary.to_a.each { |obj| push_back(obj) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns true if the Deque is empty, false otherwise.
|
47
|
+
def empty?
|
48
|
+
@size == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# Removes all the objects in the Deque.
|
52
|
+
def clear
|
53
|
+
@front = @back = nil
|
54
|
+
@size = 0
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return the number of items in the Deque.
|
58
|
+
#
|
59
|
+
# d = Containers::Deque.new([1, 2, 3])
|
60
|
+
# d.size #=> 3
|
61
|
+
def size
|
62
|
+
@size
|
63
|
+
end
|
64
|
+
alias_method :length, :size
|
65
|
+
|
66
|
+
# Returns the object at the front of the Deque but does not remove it.
|
67
|
+
#
|
68
|
+
# d = Containers::Deque.new
|
69
|
+
# d.push_front(1)
|
70
|
+
# d.push_front(2)
|
71
|
+
# d.front #=> 2
|
72
|
+
def front
|
73
|
+
@front && @front.obj
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the object at the back of the Deque but does not remove it.
|
77
|
+
#
|
78
|
+
# d = Containers::Deque.new
|
79
|
+
# d.push_front(1)
|
80
|
+
# d.push_front(2)
|
81
|
+
# d.back #=> 1
|
82
|
+
def back
|
83
|
+
@back && @back.obj
|
84
|
+
end
|
85
|
+
|
86
|
+
# Adds an object at the front of the Deque.
|
87
|
+
#
|
88
|
+
# d = Containers::Deque.new([1, 2, 3])
|
89
|
+
# d.push_front(0)
|
90
|
+
# d.pop_front #=> 0
|
91
|
+
def push_front(obj)
|
92
|
+
node = Node.new(nil, nil, obj)
|
93
|
+
if @front
|
94
|
+
node.right = @front
|
95
|
+
@front.left = node
|
96
|
+
@front = node
|
97
|
+
else
|
98
|
+
@front = @back = node
|
99
|
+
end
|
100
|
+
@size += 1
|
101
|
+
obj
|
102
|
+
end
|
103
|
+
|
104
|
+
# Adds an object at the back of the Deque.
|
105
|
+
#
|
106
|
+
# d = Containers::Deque.new([1, 2, 3])
|
107
|
+
# d.push_back(4)
|
108
|
+
# d.pop_back #=> 4
|
109
|
+
def push_back(obj)
|
110
|
+
node = Node.new(nil, nil, obj)
|
111
|
+
if @back
|
112
|
+
node.left = @back
|
113
|
+
@back.right = node
|
114
|
+
@back = node
|
115
|
+
else
|
116
|
+
@front = @back = node
|
117
|
+
end
|
118
|
+
@size += 1
|
119
|
+
obj
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns the object at the front of the Deque and removes it.
|
123
|
+
#
|
124
|
+
# d = Containers::Deque.new
|
125
|
+
# d.push_front(1)
|
126
|
+
# d.push_front(2)
|
127
|
+
# d.pop_front #=> 2
|
128
|
+
# d.size #=> 1
|
129
|
+
def pop_front
|
130
|
+
return nil unless @front
|
131
|
+
node = @front
|
132
|
+
if @size == 1
|
133
|
+
clear
|
134
|
+
return node.obj
|
135
|
+
else
|
136
|
+
@front.right.left = nil
|
137
|
+
@front = @front.right
|
138
|
+
end
|
139
|
+
@size -= 1
|
140
|
+
node.obj
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns the object at the back of the Deque and removes it.
|
144
|
+
#
|
145
|
+
# d = Containers::Deque.new
|
146
|
+
# d.push_front(1)
|
147
|
+
# d.push_front(2)
|
148
|
+
# d.pop_back #=> 1
|
149
|
+
# d.size #=> 1
|
150
|
+
def pop_back
|
151
|
+
return nil unless @back
|
152
|
+
node = @back
|
153
|
+
if @size == 1
|
154
|
+
clear
|
155
|
+
return node.obj
|
156
|
+
else
|
157
|
+
@back.left.right = nil
|
158
|
+
@back = @back.left
|
159
|
+
end
|
160
|
+
@size -= 1
|
161
|
+
node.obj
|
162
|
+
end
|
163
|
+
|
164
|
+
# Iterate over the Deque in FIFO order.
|
165
|
+
def each_forward
|
166
|
+
return unless @front
|
167
|
+
node = @front
|
168
|
+
while node
|
169
|
+
yield node.obj
|
170
|
+
node = node.right
|
171
|
+
end
|
172
|
+
end
|
173
|
+
alias_method :each, :each_forward
|
174
|
+
|
175
|
+
# Iterate over the Deque in LIFO order.
|
176
|
+
def each_backward
|
177
|
+
return unless @back
|
178
|
+
node = @back
|
179
|
+
while node
|
180
|
+
yield node.obj
|
181
|
+
node = node.left
|
182
|
+
end
|
183
|
+
end
|
184
|
+
alias_method :reverse_each, :each_backward
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
begin
|
189
|
+
require 'CDeque'
|
190
|
+
Containers::Deque = Containers::CDeque
|
191
|
+
rescue LoadError # C Version could not be found, try ruby version
|
192
|
+
Containers::Deque = Containers::RubyDeque
|
193
|
+
end
|