avl_tree 1.0.0 → 1.1.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/bench/bench.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  require 'benchmark'
2
2
  require 'radix_tree' # gem install radix_tree
3
3
  require 'avl_tree'
4
+ require 'red_black_tree'
5
+ require 'openssl'
4
6
 
5
- random = Random.new(0)
7
+ #random = Random.new(0)
6
8
 
7
9
  TIMES = 100000
8
10
  key_size = 10
@@ -40,11 +42,12 @@ end
40
42
 
41
43
  keys = []
42
44
  TIMES.times do
43
- keys << random.bytes(key_size)
45
+ keys << OpenSSL::Random.random_bytes(key_size)
44
46
  end
45
47
 
46
48
  Benchmark.bmbm do |bm|
47
49
  run(bm, Hash.new, keys)
48
50
  run(bm, RadixTree.new, keys)
49
51
  run(bm, AVLTree.new, keys)
52
+ run(bm, RedBlackTree.new, keys)
50
53
  end
@@ -1,8 +1,7 @@
1
1
  require 'benchmark'
2
2
  require 'radix_tree' # gem install radix_tree
3
3
  require 'avl_tree'
4
-
5
- random = Random.new(0)
4
+ require 'openssl'
6
5
 
7
6
  times = 100000
8
7
  key_size = 10
@@ -38,15 +37,17 @@ def run(bm, h, keys)
38
37
  end
39
38
  end
40
39
 
41
- [10000, 20000, 50000, 100000, 200000, 500000].each do |elements|
42
- keys = []
43
- elements.times do
44
- keys << random.bytes(key_size)
45
- end
40
+ keys = []
41
+ 1000000.times do
42
+ keys << OpenSSL::Random.random_bytes(key_size)
43
+ end
44
+
45
+ 1.upto(100) do |idx|
46
+ elements = idx * 10000
46
47
 
47
48
  Benchmark.bm(30) do |bm|
48
- run(bm, Hash.new, keys)
49
- run(bm, RadixTree.new, keys)
50
- run(bm, AVLTree.new, keys)
49
+ #run(bm, Hash.new, keys[0, elements])
50
+ #run(bm, RadixTree.new, keys)
51
+ run(bm, AVLTree.new, keys[0, elements])
51
52
  end
52
53
  end
data/bench/profile.rb CHANGED
@@ -1,13 +1,13 @@
1
- require File.expand_path('../lib/avl_tree', File.dirname(__FILE__))
1
+ require File.expand_path('../lib/red_black_tree', File.dirname(__FILE__))
2
2
 
3
3
  random = Random.new(0)
4
4
 
5
- TIMES = 200000
5
+ TIMES = 50000
6
6
  key_size = 10
7
7
 
8
- h = AVLTree.new
8
+ h = RedBlackTree.new
9
9
  TIMES.times do
10
10
  h[random.bytes(key_size)] = 1
11
- h[random.bytes(key_size)]
12
- h.delete(random.bytes(key_size))
11
+ #h[random.bytes(key_size)]
12
+ #h.delete(random.bytes(key_size))
13
13
  end
data/lib/avl_tree.rb CHANGED
@@ -26,7 +26,7 @@ class AVLTree
26
26
  end
27
27
 
28
28
  # returns new_root
29
- def store(key, value)
29
+ def insert(key, value)
30
30
  Node.new(key, value)
31
31
  end
32
32
 
@@ -86,13 +86,13 @@ class AVLTree
86
86
  yield [@key, @value]
87
87
  @right.each(&block)
88
88
  end
89
-
89
+
90
90
  def each_key
91
91
  each do |k, v|
92
92
  yield k
93
93
  end
94
94
  end
95
-
95
+
96
96
  def each_value
97
97
  each do |k, v|
98
98
  yield v
@@ -108,14 +108,14 @@ class AVLTree
108
108
  end
109
109
 
110
110
  # returns new_root
111
- def store(key, value)
111
+ def insert(key, value)
112
112
  case key <=> @key
113
113
  when -1
114
- @left = @left.store(key, value)
114
+ @left = @left.insert(key, value)
115
115
  when 0
116
116
  @value = value
117
117
  when 1
118
- @right = @right.store(key, value)
118
+ @right = @right.insert(key, value)
119
119
  end
120
120
  rotate
121
121
  end
@@ -213,17 +213,15 @@ class AVLTree
213
213
  def rotate
214
214
  case @left.height - @right.height
215
215
  when +2
216
- if @left.left.height >= @left.right.height
217
- root = rotate_LL
218
- else
219
- root = rotate_LR
216
+ if @left.left.height < @left.right.height
217
+ @left = @left.rotate_left
220
218
  end
219
+ root = rotate_right
221
220
  when -2
222
- if @right.left.height <= @right.right.height
223
- root = rotate_RR
224
- else
225
- root = rotate_RL
221
+ if @right.left.height > @right.right.height
222
+ @right = @right.rotate_right
226
223
  end
224
+ root = rotate_left
227
225
  else
228
226
  root = self
229
227
  end
@@ -231,21 +229,6 @@ class AVLTree
231
229
  root
232
230
  end
233
231
 
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
232
  # Right single rotation
250
233
  # (B a (D c E)) where D-a > 1 && E > c --> (D (B a c) E)
251
234
  #
@@ -255,7 +238,7 @@ class AVLTree
255
238
  # / \ / \
256
239
  # c E a c
257
240
  #
258
- def rotate_RR
241
+ def rotate_left
259
242
  root = @right
260
243
  @right = root.left
261
244
  root.left = self
@@ -272,7 +255,7 @@ class AVLTree
272
255
  # / \ / \
273
256
  # A c c e
274
257
  #
275
- def rotate_LL
258
+ def rotate_right
276
259
  root = @left
277
260
  @left = root.right
278
261
  root.right = self
@@ -280,50 +263,19 @@ class AVLTree
280
263
  root
281
264
  end
282
265
 
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
266
+ private
305
267
 
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
268
+ def delete_self
269
+ if @left.empty? and @right.empty?
270
+ deleted = EMPTY
271
+ elsif @right.height < @left.height
272
+ deleted, new_left = @left.delete_max
273
+ deleted.left, deleted.right = new_left, @right
274
+ else
275
+ deleted, new_right = @right.delete_min
276
+ deleted.left, deleted.right = @left, new_right
277
+ end
278
+ deleted
327
279
  end
328
280
 
329
281
  def collect
@@ -339,7 +291,7 @@ class AVLTree
339
291
 
340
292
  attr_accessor :default
341
293
  attr_reader :default_proc
342
-
294
+
343
295
  def initialize(default = DEFAULT, &block)
344
296
  if block && default != DEFAULT
345
297
  raise ArgumentError, 'wrong number of arguments'
@@ -403,9 +355,9 @@ class AVLTree
403
355
  end
404
356
 
405
357
  def []=(key, value)
406
- @root = @root.store(key.to_s, value)
358
+ @root = @root.insert(key.to_s, value)
407
359
  end
408
- alias store []=
360
+ alias insert []=
409
361
 
410
362
  def key?(key)
411
363
  @root.retrieve(key.to_s) != Node::UNDEFINED
@@ -0,0 +1,549 @@
1
+ class RedBlackTree
2
+ include Enumerable
3
+
4
+ class Node
5
+ UNDEFINED = Object.new
6
+
7
+ attr_reader :key, :value, :color
8
+ attr_reader :left, :right
9
+
10
+ def initialize(key, value)
11
+ @key, @value = key, value
12
+ @left = @right = EMPTY
13
+ # new node is added as RED
14
+ @color = :RED
15
+ end
16
+
17
+ def set_root
18
+ @color = :BLACK
19
+ end
20
+
21
+ def red?
22
+ @color == :RED
23
+ end
24
+
25
+ def black?
26
+ @color == :BLACK
27
+ end
28
+
29
+ def empty?
30
+ false
31
+ end
32
+
33
+ def size
34
+ @left.size + 1 + @right.size
35
+ end
36
+
37
+ # inorder
38
+ def each(&block)
39
+ @left.each(&block)
40
+ yield [@key, @value]
41
+ @right.each(&block)
42
+ end
43
+
44
+ def each_key
45
+ each do |k, v|
46
+ yield k
47
+ end
48
+ end
49
+
50
+ def each_value
51
+ each do |k, v|
52
+ yield v
53
+ end
54
+ end
55
+
56
+ def keys
57
+ collect { |k, v| k }
58
+ end
59
+
60
+ def values
61
+ collect { |k, v| v }
62
+ end
63
+
64
+ # returns new_root
65
+ def insert(key, value)
66
+ ret = self
67
+ case key <=> @key
68
+ when -1
69
+ @left = @left.insert(key, value)
70
+ if black? and @left.red? and !@left.children_both_black?
71
+ ret = rebalance_for_left_insert
72
+ end
73
+ when 0
74
+ @value = value
75
+ when 1
76
+ @right = @right.insert(key, value)
77
+ if black? and @right.red? and !@right.children_both_black?
78
+ ret = rebalance_for_right_insert
79
+ end
80
+ end
81
+ ret
82
+ end
83
+
84
+ # returns value
85
+ def retrieve(key)
86
+ case key <=> @key
87
+ when -1
88
+ @left.retrieve(key)
89
+ when 0
90
+ @value
91
+ when 1
92
+ @right.retrieve(key)
93
+ end
94
+ end
95
+
96
+ # returns [deleted_node, new_root, is_rebalance_needed]
97
+ def delete(key)
98
+ ret = self
99
+ case key <=> @key
100
+ when -1
101
+ deleted, @left, rebalance = @left.delete(key)
102
+ if rebalance
103
+ ret, rebalance = rebalance_for_left_delete
104
+ end
105
+ when 0
106
+ deleted = self
107
+ ret, rebalance = delete_self
108
+ when 1
109
+ deleted, @right, rebalance = @right.delete(key)
110
+ if rebalance
111
+ ret, rebalance = rebalance_for_right_delete
112
+ end
113
+ end
114
+ [deleted, ret, rebalance]
115
+ end
116
+
117
+ def dump_tree(io, indent = '')
118
+ @right.dump_tree(io, indent + ' ')
119
+ io << indent << sprintf("#<%s:0x%010x %s %s> => %s", self.class.name, __id__, @color, @key.inspect, @value.inspect) << $/
120
+ @left.dump_tree(io, indent + ' ')
121
+ end
122
+
123
+ def dump_sexp
124
+ left = @left.dump_sexp
125
+ right = @right.dump_sexp
126
+ if left or right
127
+ '(' + [@key, left || '-', right].compact.join(' ') + ')'
128
+ else
129
+ @key
130
+ end
131
+ end
132
+
133
+ # for debugging
134
+ def check_height
135
+ lh = @left.empty? ? 0 : @left.check_height
136
+ rh = @right.empty? ? 0 : @right.check_height
137
+ if red?
138
+ if @left.red? or @right.red?
139
+ puts dump_tree(STDERR)
140
+ raise 'red/red assertion failed'
141
+ end
142
+ else
143
+ if lh != rh
144
+ puts dump_tree(STDERR)
145
+ raise "black height unbalanced: #{lh} #{rh}"
146
+ end
147
+ end
148
+ (lh > rh ? lh : rh) + (black? ? 1 : 0)
149
+ end
150
+
151
+ protected
152
+
153
+ def children_both_black?
154
+ @right.black? and @left.black?
155
+ end
156
+
157
+ def color=(color)
158
+ @color = color
159
+ end
160
+
161
+ def left=(left)
162
+ @left = left
163
+ end
164
+
165
+ def right=(right)
166
+ @right = right
167
+ end
168
+
169
+ def color_flip(other)
170
+ @color, other.color = other.color, @color
171
+ end
172
+
173
+ def node_flip(other)
174
+ @left, other.left = other.left, @left
175
+ @right, other.right = other.right, @right
176
+ color_flip(other)
177
+ end
178
+
179
+ def delete_min
180
+ if @left.empty?
181
+ [self, *delete_self]
182
+ else
183
+ ret = self
184
+ deleted, @left, rebalance = @left.delete_min
185
+ if rebalance
186
+ ret, rebalance = rebalance_for_left_delete
187
+ end
188
+ [deleted, ret, rebalance]
189
+ end
190
+ end
191
+
192
+ # trying to rebalance when the left sub-tree is 1 level lower than the right
193
+ def rebalance_for_left_delete
194
+ ret = self
195
+ rebalance = false
196
+ if black?
197
+ if @right.black?
198
+ if @right.children_both_black?
199
+ # make whole sub-tree 1 level lower and ask rebalance
200
+ @right.color = :RED
201
+ rebalance = true
202
+ else
203
+ # move 1 black from the right to the left by single/double rotation
204
+ ret = balanced_rotate_left
205
+ end
206
+ else
207
+ # flip this sub-tree into another type of 3-children node
208
+ ret = rotate_left
209
+ # try to rebalance in sub-tree
210
+ ret.left, rebalance = ret.left.rebalance_for_left_delete
211
+ raise 'should not happen' if rebalance
212
+ end
213
+ else # red
214
+ if @right.children_both_black?
215
+ # make right sub-tree 1 level lower
216
+ color_flip(@right)
217
+ else
218
+ # move 1 black from the right to the left by single/double rotation
219
+ ret = balanced_rotate_left
220
+ end
221
+ end
222
+ [ret, rebalance]
223
+ end
224
+
225
+ # trying to rebalance when the right sub-tree is 1 level lower than the left
226
+ # See rebalance_for_left_delete.
227
+ def rebalance_for_right_delete
228
+ ret = self
229
+ rebalance = false
230
+ if black?
231
+ if @left.black?
232
+ if @left.children_both_black?
233
+ @left.color = :RED
234
+ rebalance = true
235
+ else
236
+ ret = balanced_rotate_right
237
+ end
238
+ else
239
+ ret = rotate_right
240
+ ret.right, rebalance = ret.right.rebalance_for_right_delete
241
+ raise 'should not happen' if rebalance
242
+ end
243
+ else # red
244
+ if @left.children_both_black?
245
+ color_flip(@left)
246
+ else
247
+ ret = balanced_rotate_right
248
+ end
249
+ end
250
+ [ret, rebalance]
251
+ end
252
+
253
+ # move 1 black from the right to the left by single/double rotation
254
+ def balanced_rotate_left
255
+ if @right.left.red? and @right.right.black?
256
+ @right = @right.rotate_right
257
+ end
258
+ ret = rotate_left
259
+ ret.right.color = ret.left.color = :BLACK
260
+ ret
261
+ end
262
+
263
+ # move 1 black from the left to the right by single/double rotation
264
+ def balanced_rotate_right
265
+ if @left.right.red? and @left.left.black?
266
+ @left = @left.rotate_left
267
+ end
268
+ ret = rotate_right
269
+ ret.right.color = ret.left.color = :BLACK
270
+ ret
271
+ end
272
+
273
+ # Right single rotation
274
+ # (b a (D c E)) where D and E are RED --> (d (B a c) E)
275
+ #
276
+ # b d
277
+ # / \ / \
278
+ # a D -> B E
279
+ # / \ / \
280
+ # c E a c
281
+ #
282
+ def rotate_left
283
+ root = @right
284
+ @right = root.left
285
+ root.left = self
286
+ root.color_flip(root.left)
287
+ root
288
+ end
289
+
290
+ # Left single rotation
291
+ # (d (B A c) e) where A and B are RED --> (b A (D c e))
292
+ #
293
+ # d b
294
+ # / \ / \
295
+ # B e -> A D
296
+ # / \ / \
297
+ # A c c e
298
+ #
299
+ def rotate_right
300
+ root = @left
301
+ @left = root.right
302
+ root.right = self
303
+ root.color_flip(root.right)
304
+ root
305
+ end
306
+
307
+ private
308
+
309
+ # trying to rebalance when the left sub-tree is 1 level higher than the right
310
+ def rebalance_for_left_insert
311
+ ret = self
312
+ if @right.red?
313
+ @color = :RED
314
+ @left.color = @right.color = :BLACK
315
+ else
316
+ if @left.right.red?
317
+ @left = @left.rotate_left
318
+ end
319
+ ret = rotate_right
320
+ end
321
+ ret
322
+ end
323
+
324
+ # trying to rebalance when the right sub-tree is 1 level higher than the left
325
+ def rebalance_for_right_insert
326
+ ret = self
327
+ if @left.red?
328
+ @color = :RED
329
+ @left.color = @right.color = :BLACK
330
+ else
331
+ if @right.left.red?
332
+ @right = @right.rotate_right
333
+ end
334
+ ret = rotate_left
335
+ end
336
+ ret
337
+ end
338
+
339
+ def delete_self
340
+ rebalance = false
341
+ if @left.empty? and @right.empty?
342
+ # just remove this node and ask rebalance to the parent
343
+ new_root = EMPTY
344
+ if black?
345
+ rebalance = true
346
+ end
347
+ elsif @left.empty? or @right.empty?
348
+ # pick the single children
349
+ new_root = @left.empty? ? @right : @left
350
+ if black?
351
+ # keep the color black
352
+ raise 'should not happen' unless new_root.red?
353
+ color_flip(new_root)
354
+ else
355
+ # just remove the red node
356
+ end
357
+ else
358
+ # pick the minimum node from the right sub-tree and replace self with it
359
+ new_root, @right, rebalance = @right.delete_min
360
+ new_root.node_flip(self)
361
+ if rebalance
362
+ new_root, rebalance = new_root.rebalance_for_right_delete
363
+ end
364
+ end
365
+ [new_root, rebalance]
366
+ end
367
+
368
+ def collect
369
+ pool = []
370
+ each do |key, value|
371
+ pool << yield(key, value)
372
+ end
373
+ pool
374
+ end
375
+
376
+ class EmptyNode
377
+ def red?
378
+ false
379
+ end
380
+
381
+ def black?
382
+ true
383
+ end
384
+
385
+ def empty?
386
+ true
387
+ end
388
+
389
+ def value
390
+ nil
391
+ end
392
+
393
+ def size
394
+ 0
395
+ end
396
+
397
+ def each(&block)
398
+ # intentionally blank
399
+ end
400
+
401
+ # returns new_root
402
+ def insert(key, value)
403
+ Node.new(key, value)
404
+ end
405
+
406
+ # returns value
407
+ def retrieve(key)
408
+ UNDEFINED
409
+ end
410
+
411
+ # returns [deleted_node, new_root, is_rebalance_needed]
412
+ def delete(key)
413
+ [self, self, false]
414
+ end
415
+
416
+ def dump_tree(io, indent = '')
417
+ # intentionally blank
418
+ end
419
+
420
+ def dump_sexp
421
+ # intentionally blank
422
+ end
423
+ end
424
+ EMPTY = Node::EmptyNode.new
425
+ end
426
+
427
+ DEFAULT = Object.new
428
+
429
+ attr_accessor :default
430
+ attr_reader :default_proc
431
+
432
+ def initialize(default = DEFAULT, &block)
433
+ if block && default != DEFAULT
434
+ raise ArgumentError, 'wrong number of arguments'
435
+ end
436
+ @root = Node::EMPTY
437
+ @default = default
438
+ @default_proc = block
439
+ end
440
+
441
+ def empty?
442
+ @root == Node::EMPTY
443
+ end
444
+
445
+ def size
446
+ @root.size
447
+ end
448
+ alias length size
449
+
450
+ def each(&block)
451
+ if block_given?
452
+ @root.each(&block)
453
+ self
454
+ else
455
+ Enumerator.new(@root)
456
+ end
457
+ end
458
+ alias each_pair each
459
+
460
+ def each_key
461
+ if block_given?
462
+ @root.each do |k, v|
463
+ yield k
464
+ end
465
+ self
466
+ else
467
+ Enumerator.new(@root, :each_key)
468
+ end
469
+ end
470
+
471
+ def each_value
472
+ if block_given?
473
+ @root.each do |k, v|
474
+ yield v
475
+ end
476
+ self
477
+ else
478
+ Enumerator.new(@root, :each_value)
479
+ end
480
+ end
481
+
482
+ def keys
483
+ @root.keys
484
+ end
485
+
486
+ def values
487
+ @root.values
488
+ end
489
+
490
+ def clear
491
+ @root = Node::EMPTY
492
+ end
493
+
494
+ def []=(key, value)
495
+ @root = @root.insert(key.to_s, value)
496
+ @root.set_root
497
+ @root.check_height if $DEBUG
498
+ end
499
+ alias insert []=
500
+
501
+ def key?(key)
502
+ @root.retrieve(key.to_s) != Node::UNDEFINED
503
+ end
504
+ alias has_key? key?
505
+
506
+ def [](key)
507
+ value = @root.retrieve(key.to_s)
508
+ if value == Node::UNDEFINED
509
+ default_value
510
+ else
511
+ value
512
+ end
513
+ end
514
+
515
+ def delete(key)
516
+ deleted, @root, rebalance = @root.delete(key.to_s)
517
+ unless empty?
518
+ @root.set_root
519
+ @root.check_height if $DEBUG
520
+ end
521
+ deleted.value
522
+ end
523
+
524
+ def dump_tree(io = '')
525
+ @root.dump_tree(io)
526
+ io << $/
527
+ io
528
+ end
529
+
530
+ def dump_sexp
531
+ @root.dump_sexp || ''
532
+ end
533
+
534
+ def to_hash
535
+ inject({}) { |r, (k, v)| r[k] = v; r }
536
+ end
537
+
538
+ private
539
+
540
+ def default_value
541
+ if @default != DEFAULT
542
+ @default
543
+ elsif @default_proc
544
+ @default_proc.call
545
+ else
546
+ nil
547
+ end
548
+ end
549
+ end