avl_tree 1.0.0 → 1.1.0

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