algorithms 0.6.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/kanwei/algorithms.png)](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
|
|