algorithms 0.6.1 → 1.0.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 +7 -0
- data/README.markdown +4 -22
- data/Rakefile +4 -0
- data/algorithms.gemspec +2 -3
- data/ext/algorithms/string/string.c +2 -2
- data/ext/containers/bst/bst.c +1 -1
- data/ext/containers/deque/deque.c +2 -2
- data/ext/containers/rbtree_map/rbtree.c +6 -6
- data/ext/containers/splaytree_map/splaytree.c +4 -4
- data/lib/algorithms.rb +1 -0
- data/lib/algorithms/sort.rb +130 -0
- data/spec/deque_gc_mark_spec.rb +1 -1
- data/spec/deque_spec.rb +18 -18
- data/spec/heap_spec.rb +25 -25
- data/spec/kd_tree_spec.rb +2 -2
- data/spec/map_gc_mark_spec.rb +2 -2
- data/spec/priority_queue_spec.rb +20 -20
- data/spec/queue_spec.rb +10 -10
- data/spec/rb_tree_map_spec.rb +23 -23
- data/spec/search_spec.rb +9 -9
- data/spec/sort_spec.rb +6 -5
- data/spec/splay_tree_map_spec.rb +16 -16
- data/spec/stack_spec.rb +10 -10
- data/spec/string_spec.rb +6 -6
- data/spec/suffix_array_spec.rb +17 -17
- data/spec/trie_spec.rb +16 -16
- metadata +12 -15
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7cee1c06e078dc124212a3462d765d26ce5fbe16dd6b31ca6392388d8f51644c
|
4
|
+
data.tar.gz: 491e3540ee5567752d208bb7b8502d1ee998b55e9664c66b5cf5fc17394e10e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4a533a2053ae799f87df22b51ff93744e8128e0982239f3d6717bfb6f8ee8817d9c6ebffd6b4a2b478694a3f2f742fc4465eba242e5d2d0d23bfc0c881c1daa
|
7
|
+
data.tar.gz: 4ffaed30fc92fca18dd258102a702f8fceb1a77594ac6b086c627e1a3ab8ae1ad6650295acd0fb423a6ce441b6b7ab686bb56ae9ff937448d031f319d937a427
|
data/README.markdown
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# algorithms [](https://travis-ci.org/kanwei/algorithms)
|
2
2
|
|
3
|
+
[API Documentation](http://kanwei.github.io/algorithms/)
|
4
|
+
|
3
5
|
## DESCRIPTION:
|
4
6
|
|
5
7
|
Started as a [Google Summer of Code 2008](http://code.google.com/soc/2008/ruby/about.html) project
|
@@ -41,6 +43,7 @@ compare performance in different situations.
|
|
41
43
|
- Shell sort Algorithms::Sort.shell_sort
|
42
44
|
- Quicksort Algorithms::Sort.quicksort
|
43
45
|
- Mergesort Algorithms::Sort.mergesort
|
46
|
+
- Dual-Pivot Quicksort Algorithms::Sort.dualpivotquicksort
|
44
47
|
|
45
48
|
## SYNOPSIS:
|
46
49
|
|
@@ -60,25 +63,4 @@ compare performance in different situations.
|
|
60
63
|
|
61
64
|
## LICENSE:
|
62
65
|
|
63
|
-
(
|
64
|
-
|
65
|
-
Ruby Algorithms and Containers project is Copyright (c) 2009 Kanwei Li
|
66
|
-
|
67
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
68
|
-
a copy of this software and associated documentation files (the
|
69
|
-
'Software'), to deal in the Software without restriction, including
|
70
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
71
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
72
|
-
permit persons to whom the Software is furnished to do so, subject to
|
73
|
-
the following conditions:
|
74
|
-
|
75
|
-
The above copyright notice and this permission notice shall be
|
76
|
-
included in all copies or substantial portions of the Software.
|
77
|
-
|
78
|
-
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
79
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
80
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
81
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
82
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
83
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
84
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
66
|
+
See [LICENSE.md](LICENSE.md).
|
data/Rakefile
CHANGED
data/algorithms.gemspec
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "algorithms"
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "1.0.0"
|
6
6
|
|
7
7
|
s.authors = ["Kanwei Li"]
|
8
8
|
s.email = "kanwei@gmail.com"
|
9
9
|
s.license = 'MIT'
|
10
|
-
s.date = "
|
10
|
+
s.date = "2021-04-04"
|
11
11
|
s.summary = "Useful algorithms and data structures for Ruby. Optional C extensions."
|
12
12
|
s.description = "Heap, Priority Queue, Deque, Stack, Queue, Red-Black Trees, Splay Trees, sorting algorithms, and more"
|
13
13
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
@@ -19,5 +19,4 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.homepage = "https://github.com/kanwei/algorithms"
|
20
20
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Algorithms", "--main", "README.markdown"]
|
21
21
|
s.require_paths = ["lib", "ext"]
|
22
|
-
s.rubyforge_project = "algorithms"
|
23
22
|
end
|
@@ -26,7 +26,7 @@ long levenshtein_distance(VALUE str1, VALUE str2) {
|
|
26
26
|
s1_len++;
|
27
27
|
s2_len++;
|
28
28
|
|
29
|
-
d =
|
29
|
+
d = xmalloc(sizeof(typeof(d)) * s1_len * s2_len);
|
30
30
|
|
31
31
|
for (i = 0; i < s1_len; i++) {
|
32
32
|
d[i] = i; // d[i, 0] = i
|
@@ -49,7 +49,7 @@ long levenshtein_distance(VALUE str1, VALUE str2) {
|
|
49
49
|
}
|
50
50
|
}
|
51
51
|
i = d[s1_len * s2_len -1];
|
52
|
-
|
52
|
+
xfree(d);
|
53
53
|
return i;
|
54
54
|
}
|
55
55
|
|
data/ext/containers/bst/bst.c
CHANGED
@@ -19,7 +19,7 @@ void free_nodes(deque_node *node) {
|
|
19
19
|
deque_node *next;
|
20
20
|
while(node) {
|
21
21
|
next = node->right;
|
22
|
-
|
22
|
+
xfree(node);
|
23
23
|
node = next;
|
24
24
|
}
|
25
25
|
return;
|
@@ -71,7 +71,7 @@ static void deque_free(void *ptr) {
|
|
71
71
|
if (ptr) {
|
72
72
|
deque *deque = ptr;
|
73
73
|
free_nodes(deque->front);
|
74
|
-
|
74
|
+
xfree(deque);
|
75
75
|
}
|
76
76
|
}
|
77
77
|
|
@@ -31,7 +31,7 @@ static void recursively_free_nodes(rbtree_node *node) {
|
|
31
31
|
if(node) {
|
32
32
|
recursively_free_nodes(node->left);
|
33
33
|
recursively_free_nodes(node->right);
|
34
|
-
|
34
|
+
xfree(node);
|
35
35
|
}
|
36
36
|
return;
|
37
37
|
}
|
@@ -198,7 +198,7 @@ static rbtree_node* delete_min(rbtree_node *h, VALUE *deleted_value) {
|
|
198
198
|
if ( !h->left ) {
|
199
199
|
if(deleted_value)
|
200
200
|
*deleted_value = h->value;
|
201
|
-
|
201
|
+
xfree(h);
|
202
202
|
return NULL;
|
203
203
|
}
|
204
204
|
|
@@ -216,7 +216,7 @@ static rbtree_node* delete_max(rbtree_node *h, VALUE *deleted_value) {
|
|
216
216
|
|
217
217
|
if ( !h->right ) {
|
218
218
|
*deleted_value = h->value;
|
219
|
-
|
219
|
+
xfree(h);
|
220
220
|
return NULL;
|
221
221
|
}
|
222
222
|
|
@@ -245,7 +245,7 @@ static rbtree_node* delete(rbtree *tree, rbtree_node *node, VALUE key, VALUE *de
|
|
245
245
|
cmp = tree->compare_function(key, node->key);
|
246
246
|
if ( (cmp == 0) && !node->right ) {
|
247
247
|
*deleted_value = node->value;
|
248
|
-
|
248
|
+
xfree(node);
|
249
249
|
return NULL;
|
250
250
|
}
|
251
251
|
|
@@ -339,7 +339,7 @@ static void rbtree_mark(void *ptr) {
|
|
339
339
|
}
|
340
340
|
old = current;
|
341
341
|
current = current->next;
|
342
|
-
|
342
|
+
xfree(old);
|
343
343
|
}
|
344
344
|
}
|
345
345
|
}
|
@@ -349,7 +349,7 @@ static void rbtree_free(void *ptr) {
|
|
349
349
|
if (ptr) {
|
350
350
|
rbtree *tree = ptr;
|
351
351
|
recursively_free_nodes(tree->root);
|
352
|
-
|
352
|
+
xfree(tree);
|
353
353
|
}
|
354
354
|
}
|
355
355
|
|
@@ -28,7 +28,7 @@ static void recursively_free_nodes(splaytree_node *node) {
|
|
28
28
|
if(node) {
|
29
29
|
recursively_free_nodes(node->left);
|
30
30
|
recursively_free_nodes(node->right);
|
31
|
-
|
31
|
+
xfree(node);
|
32
32
|
}
|
33
33
|
return;
|
34
34
|
}
|
@@ -199,7 +199,7 @@ static splaytree_node* delete(splaytree *tree, splaytree_node *n, VALUE key, VAL
|
|
199
199
|
x = splay(tree, n->left, key);
|
200
200
|
x->right = n->right;
|
201
201
|
}
|
202
|
-
|
202
|
+
xfree(n);
|
203
203
|
if (x) {
|
204
204
|
x->size = tsize-1;
|
205
205
|
}
|
@@ -282,7 +282,7 @@ static void splaytree_mark(void *ptr) {
|
|
282
282
|
}
|
283
283
|
old = current;
|
284
284
|
current = current->next;
|
285
|
-
|
285
|
+
xfree(old);
|
286
286
|
}
|
287
287
|
}
|
288
288
|
}
|
@@ -292,7 +292,7 @@ static void splaytree_free(void *ptr) {
|
|
292
292
|
if (ptr) {
|
293
293
|
splaytree *tree = ptr;
|
294
294
|
recursively_free_nodes(tree->root);
|
295
|
-
|
295
|
+
xfree(tree);
|
296
296
|
}
|
297
297
|
}
|
298
298
|
|
data/lib/algorithms.rb
CHANGED
@@ -43,6 +43,7 @@
|
|
43
43
|
- Shell sort - Algorithms::Sort.shell_sort
|
44
44
|
- Quicksort - Algorithms::Sort.quicksort
|
45
45
|
- Mergesort - Algorithms::Sort.mergesort
|
46
|
+
- Dual-Pivot Quicksort - Algorithms::Sort.dualpivotquicksort
|
46
47
|
* String algorithms
|
47
48
|
- Levenshtein distance - Algorithms::String.levenshtein_dist
|
48
49
|
=end
|
data/lib/algorithms/sort.rb
CHANGED
@@ -235,4 +235,134 @@ module Algorithms::Sort
|
|
235
235
|
sorted + left + right
|
236
236
|
end
|
237
237
|
|
238
|
+
# Dual-Pivot Quicksort is a variation of Quicksort by Vladimir Yaroslavskiy.
|
239
|
+
# This is an implementation of the algorithm as it was found in the original
|
240
|
+
# research paper:
|
241
|
+
#
|
242
|
+
# http://iaroslavski.narod.ru/quicksort/DualPivotQuicksort.pdf
|
243
|
+
#
|
244
|
+
# Mirror:
|
245
|
+
# http://codeblab.com/wp-content/uploads/2009/09/DualPivotQuicksort.pdf
|
246
|
+
#
|
247
|
+
# "This algorithm offers O(n log(n)) performance on many data sets that cause
|
248
|
+
# other quicksorts to degrade to quadratic performance, and is typically
|
249
|
+
# faster than traditional (one-pivot) Quicksort implementations."
|
250
|
+
# -- http://download.oracle.com/javase/7/docs/api/java/util/Arrays.html
|
251
|
+
#
|
252
|
+
# The algorithm was improved by Vladimir Yaroslavskiy, Jon Bentley, and
|
253
|
+
# Joshua Bloch, and was implemented as the default sort algorithm for
|
254
|
+
# primatives in Java 7.
|
255
|
+
#
|
256
|
+
# Implementation in the Java JDK as of November, 2011:
|
257
|
+
# http://www.docjar.com/html/api/java/util/DualPivotQuicksort.java.html
|
258
|
+
#
|
259
|
+
# It is proved that for the Dual-Pivot Quicksort the average number
|
260
|
+
# of comparisons is 2*n*ln(n), the average number of swaps is
|
261
|
+
# 0.8*n*ln(n), whereas classical Quicksort algorithm has 2*n*ln(n)
|
262
|
+
# and 1*n*ln(n) respectively. This has been fully examined mathematically
|
263
|
+
# and experimentally.
|
264
|
+
#
|
265
|
+
# Requirements: Container should implement #pop and include the Enumerable module.
|
266
|
+
# Time Complexity: О(n log n) average, О(n log n) worst-case
|
267
|
+
# Space Complexity: О(n) auxiliary
|
268
|
+
#
|
269
|
+
# Stable: No
|
270
|
+
#
|
271
|
+
# Algorithms::Sort.dualpivotquicksort [5, 4, 3, 1, 2] => [1, 2, 3, 4, 5]
|
272
|
+
|
273
|
+
def self.dualpivotquicksort(container)
|
274
|
+
return container if container.size <= 1
|
275
|
+
dualpivot(container, 0, container.size-1, 3)
|
276
|
+
end
|
277
|
+
|
278
|
+
def self.dualpivot(container, left=0, right=container.size-1, div=3)
|
279
|
+
length = right - left
|
280
|
+
if length < 27 # insertion sort for tiny array
|
281
|
+
container.each_with_index do |data,i|
|
282
|
+
j = i - 1
|
283
|
+
while j >= 0
|
284
|
+
break if container[j] <= data
|
285
|
+
container[j + 1] = container[j]
|
286
|
+
j = j - 1
|
287
|
+
end
|
288
|
+
container[j + 1] = data
|
289
|
+
end
|
290
|
+
else # full dual-pivot quicksort
|
291
|
+
third = length / div
|
292
|
+
# medians
|
293
|
+
m1 = left + third
|
294
|
+
m2 = right - third
|
295
|
+
if m1 <= left
|
296
|
+
m1 = left + 1
|
297
|
+
end
|
298
|
+
if m2 >= right
|
299
|
+
m2 = right - 1
|
300
|
+
end
|
301
|
+
if container[m1] < container[m2]
|
302
|
+
dualpivot_swap(container, m1, left)
|
303
|
+
dualpivot_swap(container, m2, right)
|
304
|
+
else
|
305
|
+
dualpivot_swap(container, m1, right)
|
306
|
+
dualpivot_swap(container, m2, left)
|
307
|
+
end
|
308
|
+
# pivots
|
309
|
+
pivot1 = container[left]
|
310
|
+
pivot2 = container[right]
|
311
|
+
# pointers
|
312
|
+
less = left + 1
|
313
|
+
great = right - 1
|
314
|
+
# sorting
|
315
|
+
k = less
|
316
|
+
while k <= great
|
317
|
+
if container[k] < pivot1
|
318
|
+
dualpivot_swap(container, k, less += 1)
|
319
|
+
elsif container[k] > pivot2
|
320
|
+
while k < great && container[great] > pivot2
|
321
|
+
great -= 1
|
322
|
+
end
|
323
|
+
dualpivot_swap(container, k, great -= 1)
|
324
|
+
if container[k] < pivot1
|
325
|
+
dualpivot_swap(container, k, less += 1)
|
326
|
+
end
|
327
|
+
end
|
328
|
+
k += 1
|
329
|
+
end
|
330
|
+
# swaps
|
331
|
+
dist = great - less
|
332
|
+
if dist < 13
|
333
|
+
div += 1
|
334
|
+
end
|
335
|
+
dualpivot_swap(container, less-1, left)
|
336
|
+
dualpivot_swap(container, great+1, right)
|
337
|
+
# subarrays
|
338
|
+
dualpivot(container, left, less-2, div)
|
339
|
+
dualpivot(container, great+2, right, div)
|
340
|
+
# equal elements
|
341
|
+
if dist > length - 13 && pivot1 != pivot2
|
342
|
+
for k in less..great do
|
343
|
+
if container[k] == pivot1
|
344
|
+
dualpivot_swap(container, k, less)
|
345
|
+
less += 1
|
346
|
+
elsif container[k] == pivot2
|
347
|
+
dualpivot_swap(container, k, great)
|
348
|
+
great -= 1
|
349
|
+
if container[k] == pivot1
|
350
|
+
dualpivot_swap(container, k, less)
|
351
|
+
less += 1
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
# subarray
|
357
|
+
if pivot1 < pivot2
|
358
|
+
dualpivot(container, less, great, div)
|
359
|
+
end
|
360
|
+
container
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def self.dualpivot_swap(container, i, j)
|
365
|
+
container[i], container[j] = container[j], container[i]
|
366
|
+
end
|
238
367
|
end
|
368
|
+
|
data/spec/deque_gc_mark_spec.rb
CHANGED
data/spec/deque_spec.rb
CHANGED
@@ -3,31 +3,31 @@ require 'algorithms'
|
|
3
3
|
|
4
4
|
shared_examples "(empty deque)" do
|
5
5
|
it "should return nil when popping objects" do
|
6
|
-
@deque.pop_front.
|
7
|
-
@deque.pop_back.
|
6
|
+
expect(@deque.pop_front).to be_nil
|
7
|
+
expect(@deque.pop_back).to be_nil
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should return a size of 1 when sent #push_front" do
|
11
11
|
@deque.push_front(1)
|
12
|
-
@deque.size.
|
12
|
+
expect(@deque.size).to eql(1)
|
13
13
|
end
|
14
14
|
|
15
15
|
it "should return a size of 1 when sent #push_back" do
|
16
16
|
@deque.push_back(1)
|
17
|
-
@deque.size.
|
17
|
+
expect(@deque.size).to eql(1)
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should return nil when sent #front and #back" do
|
21
|
-
@deque.front.
|
22
|
-
@deque.back.
|
21
|
+
expect(@deque.front).to be_nil
|
22
|
+
expect(@deque.back).to be_nil
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should be empty" do
|
26
|
-
@deque.
|
26
|
+
expect(@deque).to be_empty
|
27
27
|
end
|
28
28
|
|
29
29
|
it "should raise ArgumentError if passed more than one argument" do
|
30
|
-
|
30
|
+
expect { @deque.class.send("new", Time.now, []) }.to raise_error(ArgumentError)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -38,40 +38,40 @@ shared_examples "(non-empty deque)" do
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it "should return last pushed object with pop_back" do
|
41
|
-
@deque.pop_back.
|
42
|
-
@deque.pop_back.
|
41
|
+
expect(@deque.pop_back).to eql("10")
|
42
|
+
expect(@deque.pop_back).to eql(10)
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should return first pushed object with pop_front" do
|
46
|
-
@deque.pop_front.
|
47
|
-
@deque.pop_front.
|
46
|
+
expect(@deque.pop_front).to eql(10)
|
47
|
+
expect(@deque.pop_front).to eql("10")
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should return a size greater than 0" do
|
51
|
-
@deque.size.
|
51
|
+
expect(@deque.size).to eql(2)
|
52
52
|
end
|
53
53
|
|
54
54
|
it "should not be empty" do
|
55
|
-
@deque.
|
55
|
+
expect(@deque).not_to be_empty
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should iterate in LIFO order with #each_backward" do
|
59
59
|
arr = []
|
60
60
|
@deque.each_backward { |obj| arr << obj }
|
61
|
-
arr.
|
61
|
+
expect(arr).to eql(["10", 10])
|
62
62
|
end
|
63
63
|
|
64
64
|
it "should iterate in FIFO order with #each_forward" do
|
65
65
|
arr = []
|
66
66
|
@deque.each_forward { |obj| arr << obj }
|
67
|
-
arr.
|
67
|
+
expect(arr).to eql([10, "10"])
|
68
68
|
end
|
69
69
|
|
70
70
|
it "should return nil after everything's popped" do
|
71
71
|
@deque.pop_back
|
72
72
|
@deque.pop_back
|
73
|
-
@deque.pop_back.
|
74
|
-
@deque.front.
|
73
|
+
expect(@deque.pop_back).to be_nil
|
74
|
+
expect(@deque.front).to be_nil
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|