algorithms 0.1.0 → 0.2.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.
- data/History.txt +8 -0
- data/Manifest +9 -14
- data/README.markdown +92 -0
- data/Rakefile +3 -2
- data/algorithms.gemspec +6 -6
- data/benchmarks/deque.rb +17 -0
- data/benchmarks/sorts.rb +3 -2
- data/benchmarks/treemaps.rb +36 -0
- data/ext/containers/deque/deque.c +18 -4
- data/ext/containers/{tree_map → rbtree_map}/extconf.rb +0 -0
- data/ext/containers/{tree_map → rbtree_map}/rbtree.c +42 -12
- data/ext/containers/{bst → splaytree_map}/extconf.rb +1 -1
- data/ext/containers/splaytree_map/splaytree.c +370 -0
- data/lib/algorithms.rb +6 -5
- data/lib/containers/deque.rb +5 -10
- data/lib/containers/splay_tree_map.rb +14 -19
- data/spec/deque_gc_mark_spec.rb +18 -0
- data/spec/heap_spec.rb +110 -103
- data/spec/kd_tree_spec.rb +1 -86
- data/spec/priority_queue_spec.rb +62 -62
- data/spec/rb_tree_map_gc_mark_spec.rb +25 -0
- data/spec/splay_tree_map_spec.rb +26 -26
- metadata +19 -25
- data/README +0 -90
- data/benchmark.rb +0 -51
- data/benchmarks/rbench.rb +0 -16
- data/benchmarks/rbench/column.rb +0 -26
- data/benchmarks/rbench/group.rb +0 -43
- data/benchmarks/rbench/report.rb +0 -53
- data/benchmarks/rbench/runner.rb +0 -109
- data/benchmarks/rbench/summary.rb +0 -51
- data/ext/containers/bst/bst.c +0 -205
- data/lib/graphs/graph.rb +0 -25
- data/spec/bst_spec.rb +0 -31
data/lib/algorithms.rb
CHANGED
@@ -45,11 +45,12 @@
|
|
45
45
|
=end
|
46
46
|
|
47
47
|
module Algorithms; end
|
48
|
-
module Containers
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
module Containers; end
|
49
|
+
|
50
|
+
begin
|
51
|
+
require 'CBst'
|
52
|
+
Containers::Bst = Containers::CBst
|
53
|
+
rescue LoadError # C Version could not be found
|
53
54
|
end
|
54
55
|
|
55
56
|
require 'algorithms/search'
|
data/lib/containers/deque.rb
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
=end
|
7
7
|
class Containers::RubyDeque
|
8
8
|
include Enumerable
|
9
|
+
|
10
|
+
Node = Struct.new(:left, :right, :obj)
|
11
|
+
|
9
12
|
# Create a new Deque. Takes an optional array argument to initialize the Deque.
|
10
13
|
#
|
11
14
|
# d = Containers::Deque.new([1, 2, 3])
|
@@ -64,7 +67,7 @@ class Containers::RubyDeque
|
|
64
67
|
# d.push_front(0)
|
65
68
|
# d.pop_front #=> 0
|
66
69
|
def push_front(obj)
|
67
|
-
node = Node.new(obj)
|
70
|
+
node = Node.new(nil, nil, obj)
|
68
71
|
if @front
|
69
72
|
node.right = @front
|
70
73
|
@front.left = node
|
@@ -82,7 +85,7 @@ class Containers::RubyDeque
|
|
82
85
|
# d.push_back(4)
|
83
86
|
# d.pop_back #=> 4
|
84
87
|
def push_back(obj)
|
85
|
-
node = Node.new(obj)
|
88
|
+
node = Node.new(nil, nil, obj)
|
86
89
|
if @back
|
87
90
|
node.left = @back
|
88
91
|
@back.right = node
|
@@ -158,14 +161,6 @@ class Containers::RubyDeque
|
|
158
161
|
end
|
159
162
|
alias_method :reverse_each, :each_backward
|
160
163
|
|
161
|
-
class Node # :nodoc: all
|
162
|
-
attr_accessor :left, :right, :obj
|
163
|
-
|
164
|
-
def initialize(obj)
|
165
|
-
@left = @right = nil
|
166
|
-
@obj = obj
|
167
|
-
end
|
168
|
-
end
|
169
164
|
end
|
170
165
|
|
171
166
|
begin
|
@@ -13,11 +13,14 @@ require 'containers/stack'
|
|
13
13
|
when keys are added in sorted order, causing the tree to have a height of the number of items added.
|
14
14
|
|
15
15
|
=end
|
16
|
-
class Containers::
|
16
|
+
class Containers::RubySplayTreeMap
|
17
17
|
include Enumerable
|
18
18
|
|
19
|
+
Node = Struct.new(:key, :value, :left, :right)
|
20
|
+
|
19
21
|
# Create and initialize a new empty SplayTreeMap.
|
20
22
|
def initialize
|
23
|
+
@size = 0
|
21
24
|
clear
|
22
25
|
end
|
23
26
|
|
@@ -30,7 +33,7 @@ class Containers::SplayTreeMap
|
|
30
33
|
# map.get("MA") #=> "Massachusetts"
|
31
34
|
def push(key, value)
|
32
35
|
if @root.nil?
|
33
|
-
@root = Node.new(key, value)
|
36
|
+
@root = Node.new(key, value, nil, nil)
|
34
37
|
@size = 1
|
35
38
|
return value
|
36
39
|
end
|
@@ -41,7 +44,7 @@ class Containers::SplayTreeMap
|
|
41
44
|
@root.value = value
|
42
45
|
return value
|
43
46
|
end
|
44
|
-
node = Node.new(key, value)
|
47
|
+
node = Node.new(key, value, nil, nil)
|
45
48
|
if cmp < 1
|
46
49
|
node.left = @root.left
|
47
50
|
node.right = @root
|
@@ -74,7 +77,7 @@ class Containers::SplayTreeMap
|
|
74
77
|
def clear
|
75
78
|
@root = nil
|
76
79
|
@size = 0
|
77
|
-
@header = Node.new(nil, nil)
|
80
|
+
@header = Node.new(nil, nil, nil, nil)
|
78
81
|
end
|
79
82
|
|
80
83
|
# Return the height of the tree structure in the SplayTreeMap.
|
@@ -203,20 +206,6 @@ class Containers::SplayTreeMap
|
|
203
206
|
end
|
204
207
|
end
|
205
208
|
|
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
209
|
# Moves a key to the root, updating the structure in each step.
|
221
210
|
def splay(key)
|
222
211
|
l, r = @header, @header
|
@@ -270,5 +259,11 @@ class Containers::SplayTreeMap
|
|
270
259
|
left_height > right_height ? left_height : right_height
|
271
260
|
end
|
272
261
|
private :height_recursive
|
262
|
+
end
|
273
263
|
|
274
|
-
|
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,18 @@
|
|
1
|
+
$: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
|
2
|
+
require 'algorithms'
|
3
|
+
|
4
|
+
if defined? Containers::CDeque
|
5
|
+
describe "CDeque" do
|
6
|
+
it "should mark ruby object references" do
|
7
|
+
anon_class = Class.new
|
8
|
+
@deque = Containers::CDeque.new
|
9
|
+
100.times { @deque.push_front(anon_class.new) }
|
10
|
+
# Mark and sweep
|
11
|
+
ObjectSpace.garbage_collect
|
12
|
+
# Check if any instances were swept
|
13
|
+
count = 0
|
14
|
+
ObjectSpace.each_object(anon_class) { |x| count += 1 }
|
15
|
+
count.should eql(100)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/heap_spec.rb
CHANGED
@@ -1,119 +1,126 @@
|
|
1
1
|
$: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
|
2
2
|
require 'algorithms'
|
3
3
|
|
4
|
-
describe
|
4
|
+
describe Containers::Heap do
|
5
5
|
before(:each) do
|
6
6
|
@heap = Containers::MaxHeap.new
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
it "should not let you merge with non-heaps" do
|
10
10
|
lambda { @heap.merge!(nil) }.should raise_error
|
11
11
|
lambda { @heap.merge!([]) }.should raise_error
|
12
12
|
end
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
describe "non-empty heap" do
|
36
|
-
before(:each) do
|
37
|
-
@random_array = []
|
38
|
-
@num_items = 100
|
39
|
-
@num_items.times { |x| @random_array << rand(@num_items) }
|
40
|
-
@heap = Containers::MaxHeap.new(@random_array)
|
41
|
-
end
|
42
|
-
|
43
|
-
it "should display the correct size" do
|
44
|
-
@heap.size.should eql(@num_items)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should delete random keys" do
|
48
|
-
@heap.delete(@random_array[0]).should eql(@random_array[0])
|
49
|
-
@heap.delete(@random_array[1]).should eql(@random_array[1])
|
50
|
-
ordered = []
|
51
|
-
ordered << @heap.max! until @heap.empty?
|
52
|
-
ordered.should eql( @random_array[2..-1].sort.reverse )
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should delete all keys" do
|
56
|
-
ordered = []
|
57
|
-
@random_array.size.times do |t|
|
58
|
-
ordered << @heap.delete(@random_array[t])
|
13
|
+
|
14
|
+
describe "(empty)" do
|
15
|
+
|
16
|
+
it "should return nil when getting the maximum" do
|
17
|
+
@heap.max!.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should let you insert and remove one item" do
|
21
|
+
@heap.size.should eql(0)
|
22
|
+
|
23
|
+
@heap.push(1)
|
24
|
+
@heap.size.should eql(1)
|
25
|
+
|
26
|
+
@heap.max!.should eql(1)
|
27
|
+
@heap.size.should eql(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should let you initialize with an array" do
|
31
|
+
@heap = Containers::MaxHeap.new([1,2,3])
|
32
|
+
@heap.size.should eql(3)
|
59
33
|
end
|
60
|
-
@heap.should be_empty
|
61
|
-
ordered.should eql( @random_array )
|
62
|
-
end
|
63
|
-
|
64
|
-
it "should be in max->min order" do
|
65
|
-
ordered = []
|
66
|
-
ordered << @heap.max! until @heap.empty?
|
67
|
-
|
68
|
-
ordered.should eql(@random_array.sort.reverse)
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should change certain keys" do
|
72
|
-
numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
|
73
|
-
heap = Containers::MinHeap.new(numbers)
|
74
|
-
heap.change_key(101, 50)
|
75
|
-
heap.pop
|
76
|
-
heap.pop
|
77
|
-
heap.change_key(8, 0)
|
78
|
-
ordered = []
|
79
|
-
ordered << heap.min! until heap.empty?
|
80
|
-
ordered.should eql( [8,3,4,5,6,7,9,10,101,100] )
|
81
|
-
end
|
82
|
-
|
83
|
-
it "should not delete keys it doesn't have" do
|
84
|
-
@heap.delete(:nonexisting).should be_nil
|
85
|
-
@heap.size.should eql(@num_items)
|
86
|
-
end
|
87
|
-
|
88
|
-
it "should delete certain keys" do
|
89
|
-
numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
|
90
|
-
heap = Containers::MinHeap.new(numbers)
|
91
|
-
heap.delete(5)
|
92
|
-
heap.pop
|
93
|
-
heap.pop
|
94
|
-
heap.delete(100)
|
95
|
-
ordered = []
|
96
|
-
ordered << heap.min! until heap.empty?
|
97
|
-
ordered.should eql( [3,4,6,7,8,9,10,101] )
|
98
|
-
end
|
99
|
-
|
100
|
-
it "should let you merge with another heap" do
|
101
|
-
numbers = [1,2,3,4,5,6,7,8]
|
102
|
-
otherheap = Containers::MaxHeap.new(numbers)
|
103
|
-
otherheap.size.should eql(8)
|
104
|
-
@heap.merge!(otherheap)
|
105
|
-
|
106
|
-
ordered = []
|
107
|
-
ordered << @heap.max! until @heap.empty?
|
108
34
|
|
109
|
-
ordered.should eql( (@random_array + numbers).sort.reverse)
|
110
35
|
end
|
36
|
+
|
37
|
+
describe "(non-empty)" do
|
38
|
+
before(:each) do
|
39
|
+
@random_array = []
|
40
|
+
@num_items = 100
|
41
|
+
@num_items.times { |x| @random_array << rand(@num_items) }
|
42
|
+
@heap = Containers::MaxHeap.new(@random_array)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should display the correct size" do
|
46
|
+
@heap.size.should eql(@num_items)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should delete random keys" do
|
50
|
+
@heap.delete(@random_array[0]).should eql(@random_array[0])
|
51
|
+
@heap.delete(@random_array[1]).should eql(@random_array[1])
|
52
|
+
ordered = []
|
53
|
+
ordered << @heap.max! until @heap.empty?
|
54
|
+
ordered.should eql( @random_array[2..-1].sort.reverse )
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should delete all keys" do
|
58
|
+
ordered = []
|
59
|
+
@random_array.size.times do |t|
|
60
|
+
ordered << @heap.delete(@random_array[t])
|
61
|
+
end
|
62
|
+
@heap.should be_empty
|
63
|
+
ordered.should eql( @random_array )
|
64
|
+
end
|
111
65
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
66
|
+
it "should be in max->min order" do
|
67
|
+
ordered = []
|
68
|
+
ordered << @heap.max! until @heap.empty?
|
69
|
+
|
70
|
+
ordered.should eql(@random_array.sort.reverse)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should change certain keys" do
|
74
|
+
numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
|
75
|
+
heap = Containers::MinHeap.new(numbers)
|
76
|
+
heap.change_key(101, 50)
|
77
|
+
heap.pop
|
78
|
+
heap.pop
|
79
|
+
heap.change_key(8, 0)
|
80
|
+
ordered = []
|
81
|
+
ordered << heap.min! until heap.empty?
|
82
|
+
ordered.should eql( [8,3,4,5,6,7,9,10,101,100] )
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should not delete keys it doesn't have" do
|
86
|
+
@heap.delete(:nonexisting).should be_nil
|
87
|
+
@heap.size.should eql(@num_items)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should delete certain keys" do
|
91
|
+
numbers = [1,2,3,4,5,6,7,8,9,10,100,101]
|
92
|
+
heap = Containers::MinHeap.new(numbers)
|
93
|
+
heap.delete(5)
|
94
|
+
heap.pop
|
95
|
+
heap.pop
|
96
|
+
heap.delete(100)
|
97
|
+
ordered = []
|
98
|
+
ordered << heap.min! until heap.empty?
|
99
|
+
ordered.should eql( [3,4,6,7,8,9,10,101] )
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should let you merge with another heap" do
|
103
|
+
numbers = [1,2,3,4,5,6,7,8]
|
104
|
+
otherheap = Containers::MaxHeap.new(numbers)
|
105
|
+
otherheap.size.should eql(8)
|
106
|
+
@heap.merge!(otherheap)
|
107
|
+
|
108
|
+
ordered = []
|
109
|
+
ordered << @heap.max! until @heap.empty?
|
110
|
+
|
111
|
+
ordered.should eql( (@random_array + numbers).sort.reverse)
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "min-heap" do
|
115
|
+
it "should be in min->max order" do
|
116
|
+
@heap = Containers::MinHeap.new(@random_array)
|
117
|
+
ordered = []
|
118
|
+
ordered << @heap.min! until @heap.empty?
|
119
|
+
|
120
|
+
ordered.should eql(@random_array.sort)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
118
124
|
end
|
125
|
+
|
119
126
|
end
|
data/spec/kd_tree_spec.rb
CHANGED
@@ -1,89 +1,4 @@
|
|
1
1
|
$: << File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib')
|
2
2
|
require 'algorithms'
|
3
3
|
|
4
|
-
|
5
|
-
# Small world puzzle
|
6
|
-
# Kanwei Li, 2009
|
7
|
-
|
8
|
-
# Make sure input file exists and read from it
|
9
|
-
filename = ARGV[0]
|
10
|
-
unless filename && File.exist?(filename)
|
11
|
-
puts "error: must specify a valid input file"
|
12
|
-
exit
|
13
|
-
end
|
14
|
-
input = File.open(filename)
|
15
|
-
|
16
|
-
$points = [] # Store array of points as a global so we don't have to pass it around
|
17
|
-
|
18
|
-
# Populate points array
|
19
|
-
# while line = input.gets do
|
20
|
-
# break if line.empty?
|
21
|
-
# n, x, y = line.split(/\s+/)
|
22
|
-
# $points << [n.to_i, [x.to_f, y.to_f]]
|
23
|
-
# end
|
24
|
-
|
25
|
-
tree = Containers::KDTree.new($points)
|
26
|
-
p tree
|
27
|
-
|
28
|
-
$points.sort { |a, b| a[0] <=> b[0] }.each do |point|
|
29
|
-
nearest_4 = tree.find_nearest(point[1], 4)
|
30
|
-
puts "#{point[0]} #{nearest_4[1..-1].collect{ |n| n[1] }.join(',')}\n"
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
# describe "empty trie" do
|
35
|
-
# before(:each) do
|
36
|
-
# @trie = Containers::Trie.new
|
37
|
-
# end
|
38
|
-
#
|
39
|
-
# it "should not get or has_key?" do
|
40
|
-
# @trie.get("anything").should be_nil
|
41
|
-
# @trie.has_key?("anything").should be_false
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# it "should not have longest_prefix or match wildcards" do
|
45
|
-
# @trie.wildcard("an*thing").should eql([])
|
46
|
-
# @trie.longest_prefix("an*thing").should eql("")
|
47
|
-
# end
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# describe "non-empty trie" do
|
51
|
-
# before(:each) do
|
52
|
-
# @trie = Containers::Trie.new
|
53
|
-
# @trie.push("Hello", "World")
|
54
|
-
# @trie.push("Hilly", "World")
|
55
|
-
# @trie.push("Hello, brother", "World")
|
56
|
-
# @trie.push("Hello, bob", "World")
|
57
|
-
# end
|
58
|
-
#
|
59
|
-
# it "should has_key? keys it has" do
|
60
|
-
# @trie.has_key?("Hello").should be_true
|
61
|
-
# @trie.has_key?("Hello, brother").should be_true
|
62
|
-
# @trie.has_key?("Hello, bob").should be_true
|
63
|
-
# end
|
64
|
-
#
|
65
|
-
# it "should not has_key? keys it doesn't have" do
|
66
|
-
# @trie.has_key?("Nope").should be_false
|
67
|
-
# end
|
68
|
-
#
|
69
|
-
# it "should get values" do
|
70
|
-
# @trie.get("Hello").should eql("World")
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# it "should overwrite values" do
|
74
|
-
# @trie.push("Hello", "John")
|
75
|
-
# @trie.get("Hello").should eql("John")
|
76
|
-
# end
|
77
|
-
#
|
78
|
-
# it "should return longest prefix" do
|
79
|
-
# @trie.longest_prefix("Hello, brandon").should eql("Hello")
|
80
|
-
# @trie.longest_prefix("Hel").should eql("")
|
81
|
-
# @trie.longest_prefix("Hello").should eql("Hello")
|
82
|
-
# @trie.longest_prefix("Hello, bob").should eql("Hello, bob")
|
83
|
-
# end
|
84
|
-
#
|
85
|
-
# it "should match wildcards" do
|
86
|
-
# @trie.wildcard("H*ll.").should eql(["Hello", "Hilly"])
|
87
|
-
# @trie.wildcard("Hel").should eql([])
|
88
|
-
# end
|
89
|
-
# end
|
4
|
+
# TODO: KD Tree Spec
|