avl_tree 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.
- data/README +21 -0
- data/bench/bench.rb +50 -0
- data/bench/bench_element_size.rb +52 -0
- data/bench/profile.rb +13 -0
- data/lib/avl_tree.rb +454 -0
- data/test/helper.rb +7 -0
- data/test/test_avl_tree.rb +445 -0
- metadata +51 -0
data/README
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
avl_tree - Naive implementation of AVL tree for Ruby
|
2
|
+
Copyright (C) 2012 Hiroshi Nakamura <nahi@ruby-lang.org>
|
3
|
+
|
4
|
+
|
5
|
+
== TODO
|
6
|
+
|
7
|
+
It's still under development.
|
8
|
+
|
9
|
+
|
10
|
+
== Author
|
11
|
+
|
12
|
+
Name:: Hiroshi Nakamura
|
13
|
+
E-mail:: nahi@ruby-lang.org
|
14
|
+
Project web site:: http://github.com/nahi/radix_tree
|
15
|
+
|
16
|
+
|
17
|
+
== License
|
18
|
+
|
19
|
+
This program is copyrighted free software by Hiroshi Nakamura. You can
|
20
|
+
redistribute it and/or modify it under the same terms of Ruby's license;
|
21
|
+
either the dual license version in 2003, or any later version.
|
data/bench/bench.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'radix_tree' # gem install radix_tree
|
3
|
+
require 'avl_tree'
|
4
|
+
|
5
|
+
random = Random.new(0)
|
6
|
+
|
7
|
+
TIMES = 100000
|
8
|
+
key_size = 10
|
9
|
+
|
10
|
+
def aset(h, keys)
|
11
|
+
keys.each do |k|
|
12
|
+
h[k] = 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def aref(h, keys)
|
17
|
+
keys.each do |k|
|
18
|
+
h[k]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(h, keys)
|
23
|
+
keys.each do |k|
|
24
|
+
h.delete(k)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(bm, h, keys)
|
29
|
+
name = h.class.name
|
30
|
+
bm.report("#{name} aset") do
|
31
|
+
aset(h, keys)
|
32
|
+
end
|
33
|
+
bm.report("#{name} aref") do
|
34
|
+
aref(h, keys)
|
35
|
+
end
|
36
|
+
bm.report("#{name} delete") do
|
37
|
+
delete(h, keys)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
keys = []
|
42
|
+
TIMES.times do
|
43
|
+
keys << random.bytes(key_size)
|
44
|
+
end
|
45
|
+
|
46
|
+
Benchmark.bmbm do |bm|
|
47
|
+
run(bm, Hash.new, keys)
|
48
|
+
run(bm, RadixTree.new, keys)
|
49
|
+
run(bm, AVLTree.new, keys)
|
50
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'radix_tree' # gem install radix_tree
|
3
|
+
require 'avl_tree'
|
4
|
+
|
5
|
+
random = Random.new(0)
|
6
|
+
|
7
|
+
times = 100000
|
8
|
+
key_size = 10
|
9
|
+
|
10
|
+
def aset(h, keys)
|
11
|
+
keys.each do |k|
|
12
|
+
h[k] = 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def aref(h, keys)
|
17
|
+
keys.each do |k|
|
18
|
+
h[k]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(h, keys)
|
23
|
+
keys.each do |k|
|
24
|
+
h.delete(k)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(bm, h, keys)
|
29
|
+
name = h.class.name
|
30
|
+
bm.report("#{name} aset (#{keys.size})") do
|
31
|
+
aset(h, keys)
|
32
|
+
end
|
33
|
+
bm.report("#{name} aref (#{keys.size})") do
|
34
|
+
aref(h, keys)
|
35
|
+
end
|
36
|
+
bm.report("#{name} delete (#{keys.size})") do
|
37
|
+
delete(h, keys)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
[10000, 20000, 50000, 100000, 200000, 500000].each do |elements|
|
42
|
+
keys = []
|
43
|
+
elements.times do
|
44
|
+
keys << random.bytes(key_size)
|
45
|
+
end
|
46
|
+
|
47
|
+
Benchmark.bm(30) do |bm|
|
48
|
+
run(bm, Hash.new, keys)
|
49
|
+
run(bm, RadixTree.new, keys)
|
50
|
+
run(bm, AVLTree.new, keys)
|
51
|
+
end
|
52
|
+
end
|
data/bench/profile.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path('../lib/avl_tree', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
random = Random.new(0)
|
4
|
+
|
5
|
+
TIMES = 200000
|
6
|
+
key_size = 10
|
7
|
+
|
8
|
+
h = AVLTree.new
|
9
|
+
TIMES.times do
|
10
|
+
h[random.bytes(key_size)] = 1
|
11
|
+
h[random.bytes(key_size)]
|
12
|
+
h.delete(random.bytes(key_size))
|
13
|
+
end
|
data/lib/avl_tree.rb
ADDED
@@ -0,0 +1,454 @@
|
|
1
|
+
class AVLTree
|
2
|
+
include Enumerable
|
3
|
+
|
4
|
+
class Node
|
5
|
+
UNDEFINED = Object.new
|
6
|
+
|
7
|
+
class EmptyNode
|
8
|
+
def empty?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def height
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
def value
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def size
|
21
|
+
0
|
22
|
+
end
|
23
|
+
|
24
|
+
def each(&block)
|
25
|
+
# intentionally blank
|
26
|
+
end
|
27
|
+
|
28
|
+
# returns new_root
|
29
|
+
def store(key, value)
|
30
|
+
Node.new(key, value)
|
31
|
+
end
|
32
|
+
|
33
|
+
# returns value
|
34
|
+
def retrieve(key)
|
35
|
+
UNDEFINED
|
36
|
+
end
|
37
|
+
|
38
|
+
# returns [deleted_node, new_root]
|
39
|
+
def delete(key)
|
40
|
+
[self, self]
|
41
|
+
end
|
42
|
+
|
43
|
+
def dump_tree(io, indent = '')
|
44
|
+
# intentionally blank
|
45
|
+
end
|
46
|
+
|
47
|
+
def dump_sexp
|
48
|
+
# intentionally blank
|
49
|
+
end
|
50
|
+
|
51
|
+
def rotate
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_height
|
56
|
+
# intentionally blank
|
57
|
+
end
|
58
|
+
|
59
|
+
# for debugging
|
60
|
+
def check_height
|
61
|
+
# intentionally blank
|
62
|
+
end
|
63
|
+
end
|
64
|
+
EMPTY = Node::EmptyNode.new
|
65
|
+
|
66
|
+
attr_reader :key, :value, :height
|
67
|
+
attr_reader :left, :right
|
68
|
+
|
69
|
+
def initialize(key, value)
|
70
|
+
@key, @value = key, value
|
71
|
+
@left = @right = EMPTY
|
72
|
+
@height = 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def empty?
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
def size
|
80
|
+
@left.size + 1 + @right.size
|
81
|
+
end
|
82
|
+
|
83
|
+
# inorder
|
84
|
+
def each(&block)
|
85
|
+
@left.each(&block)
|
86
|
+
yield [@key, @value]
|
87
|
+
@right.each(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def each_key
|
91
|
+
each do |k, v|
|
92
|
+
yield k
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def each_value
|
97
|
+
each do |k, v|
|
98
|
+
yield v
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def keys
|
103
|
+
collect { |k, v| k }
|
104
|
+
end
|
105
|
+
|
106
|
+
def values
|
107
|
+
collect { |k, v| v }
|
108
|
+
end
|
109
|
+
|
110
|
+
# returns new_root
|
111
|
+
def store(key, value)
|
112
|
+
case key <=> @key
|
113
|
+
when -1
|
114
|
+
@left = @left.store(key, value)
|
115
|
+
when 0
|
116
|
+
@value = value
|
117
|
+
when 1
|
118
|
+
@right = @right.store(key, value)
|
119
|
+
end
|
120
|
+
rotate
|
121
|
+
end
|
122
|
+
|
123
|
+
# returns value
|
124
|
+
def retrieve(key)
|
125
|
+
case key <=> @key
|
126
|
+
when -1
|
127
|
+
@left.retrieve(key)
|
128
|
+
when 0
|
129
|
+
@value
|
130
|
+
when 1
|
131
|
+
@right.retrieve(key)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# returns [deleted_node, new_root]
|
136
|
+
def delete(key)
|
137
|
+
case key <=> @key
|
138
|
+
when -1
|
139
|
+
deleted, @left = @left.delete(key)
|
140
|
+
[deleted, self.rotate]
|
141
|
+
when 0
|
142
|
+
[self, delete_self.rotate]
|
143
|
+
when 1
|
144
|
+
deleted, @right = @right.delete(key)
|
145
|
+
[deleted, self.rotate]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def delete_min
|
150
|
+
if @left.empty?
|
151
|
+
[self, delete_self]
|
152
|
+
else
|
153
|
+
deleted, @left = @left.delete_min
|
154
|
+
[deleted, rotate]
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def delete_max
|
159
|
+
if @right.empty?
|
160
|
+
[self, delete_self]
|
161
|
+
else
|
162
|
+
deleted, @right = @right.delete_max
|
163
|
+
[deleted, rotate]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def dump_tree(io, indent = '')
|
168
|
+
@right.dump_tree(io, indent + ' ')
|
169
|
+
io << indent << sprintf("#<%s:0x%010x %d %s> => %s", self.class.name, __id__, height, @key.inspect, @value.inspect) << $/
|
170
|
+
@left.dump_tree(io, indent + ' ')
|
171
|
+
end
|
172
|
+
|
173
|
+
def dump_sexp
|
174
|
+
left = @left.dump_sexp
|
175
|
+
right = @right.dump_sexp
|
176
|
+
if left or right
|
177
|
+
'(' + [@key, left || '-', right].compact.join(' ') + ')'
|
178
|
+
else
|
179
|
+
@key
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# for debugging
|
184
|
+
def check_height
|
185
|
+
@left.check_height
|
186
|
+
@right.check_height
|
187
|
+
lh = @left.height
|
188
|
+
rh = @right.height
|
189
|
+
if (lh - rh).abs > 1
|
190
|
+
puts dump_tree(STDERR)
|
191
|
+
raise "height unbalanced: #{lh} #{height} #{rh}"
|
192
|
+
end
|
193
|
+
if (lh > rh ? lh : rh) + 1 != height
|
194
|
+
puts dump_tree(STDERR)
|
195
|
+
raise "height calc failure: #{lh} #{height} #{rh}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
protected
|
200
|
+
|
201
|
+
def left=(left)
|
202
|
+
@left = left
|
203
|
+
end
|
204
|
+
|
205
|
+
def right=(right)
|
206
|
+
@right = right
|
207
|
+
end
|
208
|
+
|
209
|
+
def update_height
|
210
|
+
@height = (@left.height > @right.height ? @left.height : @right.height) + 1
|
211
|
+
end
|
212
|
+
|
213
|
+
def rotate
|
214
|
+
case @left.height - @right.height
|
215
|
+
when +2
|
216
|
+
if @left.left.height >= @left.right.height
|
217
|
+
root = rotate_LL
|
218
|
+
else
|
219
|
+
root = rotate_LR
|
220
|
+
end
|
221
|
+
when -2
|
222
|
+
if @right.left.height <= @right.right.height
|
223
|
+
root = rotate_RR
|
224
|
+
else
|
225
|
+
root = rotate_RL
|
226
|
+
end
|
227
|
+
else
|
228
|
+
root = self
|
229
|
+
end
|
230
|
+
root.update_height
|
231
|
+
root
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def delete_self
|
237
|
+
if @left.empty? and @right.empty?
|
238
|
+
deleted = EMPTY
|
239
|
+
elsif @right.height < @left.height
|
240
|
+
deleted, new_left = @left.delete_max
|
241
|
+
deleted.left, deleted.right = new_left, @right
|
242
|
+
else
|
243
|
+
deleted, new_right = @right.delete_min
|
244
|
+
deleted.left, deleted.right = @left, new_right
|
245
|
+
end
|
246
|
+
deleted
|
247
|
+
end
|
248
|
+
|
249
|
+
# Right single rotation
|
250
|
+
# (B a (D c E)) where D-a > 1 && E > c --> (D (B a c) E)
|
251
|
+
#
|
252
|
+
# B D
|
253
|
+
# / \ / \
|
254
|
+
# a D -> B E
|
255
|
+
# / \ / \
|
256
|
+
# c E a c
|
257
|
+
#
|
258
|
+
def rotate_RR
|
259
|
+
root = @right
|
260
|
+
@right = root.left
|
261
|
+
root.left = self
|
262
|
+
root.left.update_height
|
263
|
+
root
|
264
|
+
end
|
265
|
+
|
266
|
+
# Left single rotation
|
267
|
+
# (D (B A c) e) where B-e > 1 && A > c --> (B A (D c e))
|
268
|
+
#
|
269
|
+
# D B
|
270
|
+
# / \ / \
|
271
|
+
# B e -> A D
|
272
|
+
# / \ / \
|
273
|
+
# A c c e
|
274
|
+
#
|
275
|
+
def rotate_LL
|
276
|
+
root = @left
|
277
|
+
@left = root.right
|
278
|
+
root.right = self
|
279
|
+
root.right.update_height
|
280
|
+
root
|
281
|
+
end
|
282
|
+
|
283
|
+
# Right double rotation
|
284
|
+
# (B a (F (D c e) g)) where F-a > 1 && D > g --> (D (B a c) (F e g))
|
285
|
+
#
|
286
|
+
# B D
|
287
|
+
# / \ / \
|
288
|
+
# a F -> B F
|
289
|
+
# / \ / \ / \
|
290
|
+
# D g a c e g
|
291
|
+
# / \
|
292
|
+
# c e
|
293
|
+
#
|
294
|
+
def rotate_RL
|
295
|
+
other = @right
|
296
|
+
root = other.left
|
297
|
+
@right = root.left
|
298
|
+
other.left = root.right
|
299
|
+
root.left = self
|
300
|
+
root.right = other
|
301
|
+
root.left.update_height
|
302
|
+
root.right.update_height
|
303
|
+
root
|
304
|
+
end
|
305
|
+
|
306
|
+
# Left double rotation
|
307
|
+
# (F (B a (D c e)) g) where B-g > 1 && D > a --> (d (B a c) (F e g))
|
308
|
+
#
|
309
|
+
# F D
|
310
|
+
# / \ / \
|
311
|
+
# B g -> B F
|
312
|
+
# / \ / \ / \
|
313
|
+
# a D a c e g
|
314
|
+
# / \
|
315
|
+
# c e
|
316
|
+
#
|
317
|
+
def rotate_LR
|
318
|
+
other = @left
|
319
|
+
root = other.right
|
320
|
+
@left = root.right
|
321
|
+
other.right = root.left
|
322
|
+
root.right = self
|
323
|
+
root.left = other
|
324
|
+
root.left.update_height
|
325
|
+
root.right.update_height
|
326
|
+
root
|
327
|
+
end
|
328
|
+
|
329
|
+
def collect
|
330
|
+
pool = []
|
331
|
+
each do |key, value|
|
332
|
+
pool << yield(key, value)
|
333
|
+
end
|
334
|
+
pool
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
DEFAULT = Object.new
|
339
|
+
|
340
|
+
attr_accessor :default
|
341
|
+
attr_reader :default_proc
|
342
|
+
|
343
|
+
def initialize(default = DEFAULT, &block)
|
344
|
+
if block && default != DEFAULT
|
345
|
+
raise ArgumentError, 'wrong number of arguments'
|
346
|
+
end
|
347
|
+
@root = Node::EMPTY
|
348
|
+
@default = default
|
349
|
+
@default_proc = block
|
350
|
+
end
|
351
|
+
|
352
|
+
def empty?
|
353
|
+
@root == Node::EMPTY
|
354
|
+
end
|
355
|
+
|
356
|
+
def size
|
357
|
+
@root.size
|
358
|
+
end
|
359
|
+
alias length size
|
360
|
+
|
361
|
+
def each(&block)
|
362
|
+
if block_given?
|
363
|
+
@root.each(&block)
|
364
|
+
self
|
365
|
+
else
|
366
|
+
Enumerator.new(@root)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
alias each_pair each
|
370
|
+
|
371
|
+
def each_key
|
372
|
+
if block_given?
|
373
|
+
@root.each do |k, v|
|
374
|
+
yield k
|
375
|
+
end
|
376
|
+
self
|
377
|
+
else
|
378
|
+
Enumerator.new(@root, :each_key)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def each_value
|
383
|
+
if block_given?
|
384
|
+
@root.each do |k, v|
|
385
|
+
yield v
|
386
|
+
end
|
387
|
+
self
|
388
|
+
else
|
389
|
+
Enumerator.new(@root, :each_value)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def keys
|
394
|
+
@root.keys
|
395
|
+
end
|
396
|
+
|
397
|
+
def values
|
398
|
+
@root.values
|
399
|
+
end
|
400
|
+
|
401
|
+
def clear
|
402
|
+
@root = Node::EMPTY
|
403
|
+
end
|
404
|
+
|
405
|
+
def []=(key, value)
|
406
|
+
@root = @root.store(key.to_s, value)
|
407
|
+
end
|
408
|
+
alias store []=
|
409
|
+
|
410
|
+
def key?(key)
|
411
|
+
@root.retrieve(key.to_s) != Node::UNDEFINED
|
412
|
+
end
|
413
|
+
alias has_key? key?
|
414
|
+
|
415
|
+
def [](key)
|
416
|
+
value = @root.retrieve(key.to_s)
|
417
|
+
if value == Node::UNDEFINED
|
418
|
+
default_value
|
419
|
+
else
|
420
|
+
value
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def delete(key)
|
425
|
+
deleted, @root = @root.delete(key.to_s)
|
426
|
+
deleted.value
|
427
|
+
end
|
428
|
+
|
429
|
+
def dump_tree(io = '')
|
430
|
+
@root.dump_tree(io)
|
431
|
+
io << $/
|
432
|
+
io
|
433
|
+
end
|
434
|
+
|
435
|
+
def dump_sexp
|
436
|
+
@root.dump_sexp || ''
|
437
|
+
end
|
438
|
+
|
439
|
+
def to_hash
|
440
|
+
inject({}) { |r, (k, v)| r[k] = v; r }
|
441
|
+
end
|
442
|
+
|
443
|
+
private
|
444
|
+
|
445
|
+
def default_value
|
446
|
+
if @default != DEFAULT
|
447
|
+
@default
|
448
|
+
elsif @default_proc
|
449
|
+
@default_proc.call
|
450
|
+
else
|
451
|
+
nil
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,445 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('./helper', File.dirname(__FILE__))
|
3
|
+
|
4
|
+
class TestAVLTree < Test::Unit::TestCase
|
5
|
+
def test_tree_rotate_RR
|
6
|
+
h = AVLTree.new
|
7
|
+
assert_equal '', h.dump_sexp
|
8
|
+
h['a'] = 1
|
9
|
+
assert_equal 'a', h.dump_sexp
|
10
|
+
h['b'] = 2
|
11
|
+
assert_equal '(a - b)', h.dump_sexp
|
12
|
+
h['c'] = 3
|
13
|
+
assert_equal '(b a c)', h.dump_sexp
|
14
|
+
h['d'] = 4
|
15
|
+
assert_equal '(b a (c - d))', h.dump_sexp
|
16
|
+
h['e'] = 5
|
17
|
+
assert_equal '(b a (d c e))', h.dump_sexp
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_tree_rotate_LL
|
21
|
+
h = AVLTree.new
|
22
|
+
h['e'] = 1
|
23
|
+
h['d'] = 2
|
24
|
+
assert_equal '(e d)', h.dump_sexp
|
25
|
+
h['c'] = 3
|
26
|
+
assert_equal '(d c e)', h.dump_sexp
|
27
|
+
h['b'] = 4
|
28
|
+
assert_equal '(d (c b) e)', h.dump_sexp
|
29
|
+
h['a'] = 5
|
30
|
+
assert_equal '(d (b a c) e)', h.dump_sexp
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_tree_rotate_RL
|
34
|
+
h = AVLTree.new
|
35
|
+
h['b'] = 1
|
36
|
+
h['a'] = 2
|
37
|
+
h['e'] = 3
|
38
|
+
h['d'] = 4
|
39
|
+
h['f'] = 5
|
40
|
+
assert_equal '(b a (e d f))', h.dump_sexp
|
41
|
+
h['c'] = 6
|
42
|
+
assert_equal '(d (b a c) (e - f))', h.dump_sexp
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_tree_rotate_LR
|
46
|
+
h = AVLTree.new
|
47
|
+
h['g'] = 1
|
48
|
+
h['b'] = 2
|
49
|
+
h['h'] = 3
|
50
|
+
h['i'] = 4
|
51
|
+
h['a'] = 5
|
52
|
+
h['d'] = 6
|
53
|
+
h['0'] = 7
|
54
|
+
h['c'] = 8
|
55
|
+
h['e'] = 9
|
56
|
+
assert_equal '(g (b (a 0) (d c e)) (h - i))', h.dump_sexp
|
57
|
+
h['f'] = 10
|
58
|
+
assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_aref_nil
|
62
|
+
h = AVLTree.new
|
63
|
+
h['abc'] = 1
|
64
|
+
assert_equal nil, h['def']
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_empty
|
68
|
+
h = AVLTree.new
|
69
|
+
h['abc'] = 0
|
70
|
+
assert_equal nil, h['']
|
71
|
+
h[''] = 1
|
72
|
+
assert_equal 1, h['']
|
73
|
+
h.delete('')
|
74
|
+
assert_equal nil, h['']
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_aref_single
|
78
|
+
h = AVLTree.new
|
79
|
+
h['abc'] = 1
|
80
|
+
assert_equal 1, h['abc']
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_aref_double
|
84
|
+
h = AVLTree.new
|
85
|
+
h['abc'] = 1
|
86
|
+
h['def'] = 2
|
87
|
+
assert_equal 1, h['abc']
|
88
|
+
assert_equal 2, h['def']
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_aset_override
|
92
|
+
h = AVLTree.new
|
93
|
+
h['abc'] = 1
|
94
|
+
h['abc'] = 2
|
95
|
+
assert_equal 2, h['abc']
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_split
|
99
|
+
h = AVLTree.new
|
100
|
+
h['abcd'] = 1
|
101
|
+
assert_equal 1, h['abcd']
|
102
|
+
h['abce'] = 2
|
103
|
+
assert_equal 1, h['abcd']
|
104
|
+
assert_equal 2, h['abce']
|
105
|
+
h['abd'] = 3
|
106
|
+
assert_equal 1, h['abcd']
|
107
|
+
assert_equal 2, h['abce']
|
108
|
+
assert_equal 3, h['abd']
|
109
|
+
h['ac'] = 4
|
110
|
+
assert_equal 1, h['abcd']
|
111
|
+
assert_equal 2, h['abce']
|
112
|
+
assert_equal 3, h['abd']
|
113
|
+
assert_equal 4, h['ac']
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_split_and_assign
|
117
|
+
h = AVLTree.new
|
118
|
+
h['ab'] = 1
|
119
|
+
h['a'] = 2
|
120
|
+
assert_equal 1, h['ab']
|
121
|
+
assert_equal 2, h['a']
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_push
|
125
|
+
h = AVLTree.new
|
126
|
+
assert_equal 0, h.size
|
127
|
+
h['a'] = 1
|
128
|
+
assert_equal 1, h['a']
|
129
|
+
h['ab'] = 2
|
130
|
+
assert_equal 1, h['a']
|
131
|
+
assert_equal 2, h['ab']
|
132
|
+
h['abc'] = 3
|
133
|
+
assert_equal 1, h['a']
|
134
|
+
assert_equal 2, h['ab']
|
135
|
+
assert_equal 3, h['abc']
|
136
|
+
h['abd'] = 4
|
137
|
+
assert_equal 1, h['a']
|
138
|
+
assert_equal 2, h['ab']
|
139
|
+
assert_equal 3, h['abc']
|
140
|
+
assert_equal 4, h['abd']
|
141
|
+
h['ac'] = 5
|
142
|
+
assert_equal 1, h['a']
|
143
|
+
assert_equal 2, h['ab']
|
144
|
+
assert_equal 3, h['abc']
|
145
|
+
assert_equal 4, h['abd']
|
146
|
+
assert_equal 5, h['ac']
|
147
|
+
h['b'] = 6
|
148
|
+
assert_equal 1, h['a']
|
149
|
+
assert_equal 2, h['ab']
|
150
|
+
assert_equal 3, h['abc']
|
151
|
+
assert_equal 4, h['abd']
|
152
|
+
assert_equal 5, h['ac']
|
153
|
+
assert_equal 6, h['b']
|
154
|
+
assert_equal ['a', 'ab', 'abc', 'abd', 'ac', 'b'].sort, h.keys.sort
|
155
|
+
assert_equal 6, h.size
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_delete_leaf
|
159
|
+
h = AVLTree.new
|
160
|
+
h['b'] = 1
|
161
|
+
h['a'] = 2
|
162
|
+
h['c'] = 3
|
163
|
+
assert_equal 2, h['a']
|
164
|
+
h.delete('a')
|
165
|
+
assert_equal nil, h['a']
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_delete_leaf_single_rotation
|
169
|
+
h = AVLTree.new
|
170
|
+
h['b'] = 1
|
171
|
+
h['a'] = 2
|
172
|
+
h['d'] = 3
|
173
|
+
h['c'] = 4
|
174
|
+
h['e'] = 5
|
175
|
+
assert_equal '(b a (d c e))', h.dump_sexp
|
176
|
+
h.delete('a')
|
177
|
+
assert_equal '(d (b - c) e)', h.dump_sexp
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_delete_leaf_double_rotation
|
181
|
+
h = AVLTree.new
|
182
|
+
h['b'] = 1
|
183
|
+
h['a'] = 2
|
184
|
+
h['e'] = 3
|
185
|
+
h['0'] = 4
|
186
|
+
h['c'] = 5
|
187
|
+
h['f'] = 6
|
188
|
+
h['d'] = 7
|
189
|
+
assert_equal '(b (a 0) (e (c - d) f))', h.dump_sexp
|
190
|
+
h.delete('0')
|
191
|
+
assert_equal '(c (b a) (e d f))', h.dump_sexp
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_delete_node_right
|
195
|
+
h = AVLTree.new
|
196
|
+
h['c'] = 1
|
197
|
+
h['b'] = 2
|
198
|
+
h['g'] = 3
|
199
|
+
h['a'] = 4
|
200
|
+
h['e'] = 5
|
201
|
+
h['i'] = 6
|
202
|
+
h['d'] = 7
|
203
|
+
h['f'] = 8
|
204
|
+
h['h'] = 9
|
205
|
+
h['j'] = 10
|
206
|
+
assert_equal '(c (b a) (g (e d f) (i h j)))', h.dump_sexp
|
207
|
+
h.delete('g')
|
208
|
+
assert_equal '(c (b a) (h (e d f) (i - j)))', h.dump_sexp
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_delete_node_left
|
212
|
+
h = AVLTree.new
|
213
|
+
h['c'] = 1
|
214
|
+
h['b'] = 2
|
215
|
+
h['d'] = 3
|
216
|
+
h['a'] = 4
|
217
|
+
assert_equal '(c (b a) d)', h.dump_sexp
|
218
|
+
h.delete('b')
|
219
|
+
assert_equal '(c a d)', h.dump_sexp
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_delete_root
|
223
|
+
h = AVLTree.new
|
224
|
+
h['b'] = 1
|
225
|
+
h['a'] = 2
|
226
|
+
h['c'] = 3
|
227
|
+
assert_equal 1, h['b']
|
228
|
+
assert_equal '(b a c)', h.dump_sexp
|
229
|
+
h.delete('b')
|
230
|
+
assert_equal '(c a)', h.dump_sexp
|
231
|
+
assert_equal nil, h['b']
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_delete
|
235
|
+
h = AVLTree.new
|
236
|
+
h['a'] = 1
|
237
|
+
h['ab'] = 2
|
238
|
+
h['abc'] = 3
|
239
|
+
h['abd'] = 4
|
240
|
+
h['ac'] = 5
|
241
|
+
h['b'] = 6
|
242
|
+
assert_equal 6, h.size
|
243
|
+
assert_equal nil, h.delete('XXX')
|
244
|
+
# delete leaf
|
245
|
+
assert_equal 4, h.delete('abd')
|
246
|
+
assert_equal 5, h.size
|
247
|
+
assert_equal 1, h['a']
|
248
|
+
assert_equal 2, h['ab']
|
249
|
+
assert_equal 3, h['abc']
|
250
|
+
assert_equal nil, h['abd']
|
251
|
+
assert_equal 5, h['ac']
|
252
|
+
assert_equal 6, h['b']
|
253
|
+
# delete single leaf node
|
254
|
+
assert_equal 2, h.delete('ab')
|
255
|
+
assert_equal 4, h.size
|
256
|
+
assert_equal 1, h['a']
|
257
|
+
assert_equal nil, h['ab']
|
258
|
+
assert_equal 3, h['abc']
|
259
|
+
assert_equal nil, h['abd']
|
260
|
+
assert_equal 5, h['ac']
|
261
|
+
assert_equal 6, h['b']
|
262
|
+
# delete multiple leaf node
|
263
|
+
assert_equal 1, h.delete('a')
|
264
|
+
assert_equal 3, h.size
|
265
|
+
assert_equal nil, h['a']
|
266
|
+
assert_equal nil, h['ab']
|
267
|
+
assert_equal 3, h['abc']
|
268
|
+
assert_equal nil, h['abd']
|
269
|
+
assert_equal 5, h['ac']
|
270
|
+
assert_equal 6, h['b']
|
271
|
+
assert_equal ['abc', 'ac', 'b'].sort, h.keys.sort
|
272
|
+
# delete rest
|
273
|
+
assert_equal 3, h.delete('abc')
|
274
|
+
assert_equal 5, h.delete('ac')
|
275
|
+
assert_equal 6, h.delete('b')
|
276
|
+
assert_equal 0, h.size
|
277
|
+
assert h.empty?
|
278
|
+
end
|
279
|
+
|
280
|
+
def test_delete_compaction_middle
|
281
|
+
h = AVLTree.new
|
282
|
+
h['a'] = 1
|
283
|
+
h['abc'] = 2
|
284
|
+
h['bb'] = 3
|
285
|
+
h['abcdefghi'] = 4
|
286
|
+
h['abcdefghijzz'] = 5
|
287
|
+
h['abcdefghikzz'] = 6
|
288
|
+
assert_equal 6, h.dump_tree.split($/).size
|
289
|
+
h.delete('a')
|
290
|
+
assert_equal 5, h.dump_tree.split($/).size
|
291
|
+
h['a'] = 1
|
292
|
+
assert_equal 6, h.dump_tree.split($/).size
|
293
|
+
end
|
294
|
+
|
295
|
+
def test_delete_compaction_leaf
|
296
|
+
h = AVLTree.new
|
297
|
+
h['a'] = 1
|
298
|
+
h['abc'] = 2
|
299
|
+
h['bb'] = 3
|
300
|
+
h['abcdefghijzz'] = 4
|
301
|
+
assert_equal 4, h.dump_tree.split($/).size
|
302
|
+
h['abcdefghikzz'] = 5
|
303
|
+
assert_equal 5, h.dump_tree.split($/).size
|
304
|
+
h.delete('abcdefghijzz')
|
305
|
+
assert_equal 4, h.dump_tree.split($/).size
|
306
|
+
h['abcdefghijzz'] = 4
|
307
|
+
assert_equal 5, h.dump_tree.split($/).size
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_each
|
311
|
+
h = AVLTree.new
|
312
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
313
|
+
s.each do |k, v|
|
314
|
+
h[k] = v
|
315
|
+
end
|
316
|
+
assert_equal s.to_a.sort_by { |k, v| k }, h.each.sort_by { |k, v| k }
|
317
|
+
#
|
318
|
+
values = []
|
319
|
+
h.each do |k, v|
|
320
|
+
values << [k, v]
|
321
|
+
end
|
322
|
+
assert_equal s.to_a.sort_by { |k, v| k }, values.sort_by { |k, v| k }
|
323
|
+
end
|
324
|
+
|
325
|
+
def test_each_key
|
326
|
+
h = AVLTree.new
|
327
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
328
|
+
s.each do |k, v|
|
329
|
+
h[k] = v
|
330
|
+
end
|
331
|
+
assert_equal s.keys.sort, h.each_key.sort
|
332
|
+
#
|
333
|
+
values = []
|
334
|
+
h.each_key do |k|
|
335
|
+
values << k
|
336
|
+
end
|
337
|
+
assert_equal s.keys.sort, values.sort
|
338
|
+
end
|
339
|
+
|
340
|
+
def test_each_value
|
341
|
+
h = AVLTree.new
|
342
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6, 'azzzzz' => 6 }
|
343
|
+
s.each do |k, v|
|
344
|
+
h[k] = v
|
345
|
+
end
|
346
|
+
assert_equal s.values.sort, h.each_value.sort
|
347
|
+
#
|
348
|
+
values = []
|
349
|
+
h.each_value do |v|
|
350
|
+
values << v
|
351
|
+
end
|
352
|
+
assert_equal s.values.sort, values.sort
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_keys
|
356
|
+
h = AVLTree.new
|
357
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
358
|
+
s.each do |k, v|
|
359
|
+
h[k] = v
|
360
|
+
end
|
361
|
+
assert_equal s.keys.sort, h.keys.sort
|
362
|
+
end
|
363
|
+
|
364
|
+
def test_values
|
365
|
+
h = AVLTree.new
|
366
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
367
|
+
s.each do |k, v|
|
368
|
+
h[k] = v
|
369
|
+
end
|
370
|
+
assert_equal s.values.sort, h.values.sort
|
371
|
+
end
|
372
|
+
|
373
|
+
def test_to_s
|
374
|
+
h = AVLTree.new
|
375
|
+
h[:abc] = 1
|
376
|
+
assert_equal 1, h["abc"]
|
377
|
+
assert_equal 1, h[:abc]
|
378
|
+
end
|
379
|
+
|
380
|
+
def test_key?
|
381
|
+
h = AVLTree.new
|
382
|
+
assert !h.key?('a')
|
383
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
384
|
+
s.each do |k, v|
|
385
|
+
h[k] = v
|
386
|
+
end
|
387
|
+
assert h.key?('a')
|
388
|
+
end
|
389
|
+
|
390
|
+
def test_default
|
391
|
+
assert_raise(ArgumentError) do
|
392
|
+
AVLTree.new('both') { :not_allowed }
|
393
|
+
end
|
394
|
+
|
395
|
+
h = AVLTree.new('abc')
|
396
|
+
assert_equal 'abc', h['foo']
|
397
|
+
assert_equal 'abc', h['bar']
|
398
|
+
assert h['baz'].object_id == h['qux'].object_id
|
399
|
+
|
400
|
+
h = AVLTree.new { [1, 2] }
|
401
|
+
assert_equal [1, 2], h['foo']
|
402
|
+
assert_equal [1, 2], h['bar']
|
403
|
+
assert h['baz'].object_id != h['qux'].object_id
|
404
|
+
end
|
405
|
+
|
406
|
+
def test_to_hash
|
407
|
+
h = AVLTree.new
|
408
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
409
|
+
s.each do |k, v|
|
410
|
+
h[k] = v
|
411
|
+
end
|
412
|
+
assert_equal s, h.to_hash
|
413
|
+
end
|
414
|
+
|
415
|
+
def test_clear
|
416
|
+
h = AVLTree.new
|
417
|
+
s = { 'aa' => 1, 'ab' => 2, 'bb' => 3, 'bc' => 4, 'a' => 5, 'abc' => 6 }
|
418
|
+
s.each do |k, v|
|
419
|
+
h[k] = v
|
420
|
+
end
|
421
|
+
assert_equal s, h.to_hash
|
422
|
+
h.clear
|
423
|
+
assert_equal 0, h.size
|
424
|
+
assert h.to_hash.empty?
|
425
|
+
end
|
426
|
+
|
427
|
+
if RUBY_VERSION >= '1.9.0'
|
428
|
+
# In contrast to RadixTree, AVLTree just uses String#<=> as-is
|
429
|
+
def test_encoding
|
430
|
+
h = AVLTree.new
|
431
|
+
s = { '$B$"$"(B' => 1, '$B$"$$(B' => 2, '$B$$$$(B' => 3, '$B$$$&(B' => 4, '$B$"(B' => 5, '$B$"$$$&(B' => 6 }
|
432
|
+
s.each do |k, v|
|
433
|
+
h[k] = v
|
434
|
+
end
|
435
|
+
assert_equal 6, h.size
|
436
|
+
s.each do |k, v|
|
437
|
+
assert_equal v, h[k]
|
438
|
+
end
|
439
|
+
str = '$B$"$"(B'
|
440
|
+
str.force_encoding('US-ASCII')
|
441
|
+
# it's nil for RadixTree because RadixTree uses char-to-char comparison
|
442
|
+
assert_equal 1, h[str]
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: avl_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Hiroshi Nakamura
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-01-13 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description:
|
15
|
+
email: nahi@ruby-lang.org
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/avl_tree.rb
|
21
|
+
- bench/bench_element_size.rb
|
22
|
+
- bench/profile.rb
|
23
|
+
- bench/bench.rb
|
24
|
+
- test/helper.rb
|
25
|
+
- test/test_avl_tree.rb
|
26
|
+
- README
|
27
|
+
homepage: http://github.com/nahi/avl_tree
|
28
|
+
licenses: []
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
require_paths:
|
32
|
+
- lib
|
33
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubyforge_project:
|
47
|
+
rubygems_version: 1.8.11
|
48
|
+
signing_key:
|
49
|
+
specification_version: 3
|
50
|
+
summary: Naive implementation of AVL tree for Ruby
|
51
|
+
test_files: []
|