algorithms 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|