avl_tree 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,7 @@
1
+ begin
2
+ require 'simplecov'
3
+ SimpleCov.start
4
+ rescue LoadError
5
+ end
6
+ require "test/unit"
7
+ require "avl_tree"
@@ -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: []