avl_tree 1.1.3 → 1.2.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.
- checksums.yaml +7 -0
- data/README.md +42 -0
- data/bench/bench_thread.rb +39 -0
- data/lib/avl_tree.rb +4 -0
- data/lib/red_black_tree.rb +334 -38
- data/test/coverage/assets/0.8.0/application.css +799 -0
- data/test/coverage/assets/0.8.0/application.js +1559 -0
- data/test/coverage/assets/0.8.0/colorbox/border.png +0 -0
- data/test/coverage/assets/0.8.0/colorbox/controls.png +0 -0
- data/test/coverage/assets/0.8.0/colorbox/loading.gif +0 -0
- data/test/coverage/assets/0.8.0/colorbox/loading_background.png +0 -0
- data/test/coverage/assets/0.8.0/favicon_green.png +0 -0
- data/test/coverage/assets/0.8.0/favicon_red.png +0 -0
- data/test/coverage/assets/0.8.0/favicon_yellow.png +0 -0
- data/test/coverage/assets/0.8.0/loading.gif +0 -0
- data/test/coverage/assets/0.8.0/magnify.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/test/coverage/assets/0.8.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/test/coverage/index.html +72 -0
- data/test/test_avl_tree.rb +37 -0
- data/test/test_red_black_tree.rb +61 -45
- data/test/test_red_black_tree_thread.rb +39 -0
- metadata +40 -21
- data/README +0 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 88542c761338424b654e97c1966f46a7d9fa2a93
|
4
|
+
data.tar.gz: 1cdba53af91bb3d3fea623c003011ad476ed7a7b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f2cd6ca32b986105d3d004130e71eec83ef8c2ca8eda35201f6e944406f49b08c61dd32c73a283295e0d30709be51af130f20da5b53be007fd3cdf5f3a70c75
|
7
|
+
data.tar.gz: 3f25b697bee9033232507f7df293f9f311a761cf2c6b09d5f38493eb5666d6faa415e8a829b3051bd2f87af31503583cfe8ebb63bd5aac04da376f9ed6cc1eba
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# AVL tree, Red-black tree in Ruby
|
2
|
+
|
3
|
+
avl_tree - AVL tree, Red-black tree and Lock-free Red black tree in Ruby
|
4
|
+
Copyright (C) 2014 Hiroshi Nakamura <nahi@ruby-lang.org>
|
5
|
+
|
6
|
+
|
7
|
+
## Usage
|
8
|
+
|
9
|
+
You can use AVLTree, RedBlackTree or ConcurrentRedBlackTree just as a
|
10
|
+
replacement of Hash.
|
11
|
+
|
12
|
+
@points = Hash.new
|
13
|
+
...
|
14
|
+
@points[score] = person
|
15
|
+
...
|
16
|
+
@points.each do |score, person|
|
17
|
+
...
|
18
|
+
end
|
19
|
+
|
20
|
+
->
|
21
|
+
|
22
|
+
require 'avl_tree'
|
23
|
+
@points = AVLTree.new
|
24
|
+
|
25
|
+
require 'red_black_tree'
|
26
|
+
@points = RedBlackTree.new
|
27
|
+
@points = ConcurrentRedBlackTree.new
|
28
|
+
|
29
|
+
AVLTree and RedBlackTree are faster but not thread-safe. Use ConcurrentRedBlackTree in multi-thread environment.
|
30
|
+
|
31
|
+
## Author
|
32
|
+
|
33
|
+
Name:: Hiroshi Nakamura
|
34
|
+
E-mail:: nahi@ruby-lang.org
|
35
|
+
Project web site:: http://github.com/nahi/avl_tree
|
36
|
+
|
37
|
+
|
38
|
+
## License
|
39
|
+
|
40
|
+
This program is copyrighted free software by Hiroshi Nakamura. You can
|
41
|
+
redistribute it and/or modify it under the same terms of Ruby's license;
|
42
|
+
either the dual license version in 2003, or any later version.
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'red_black_tree'
|
3
|
+
|
4
|
+
Benchmark.bmbm do |bm|
|
5
|
+
bm.report do
|
6
|
+
h = ConcurrentRedBlackTree.new
|
7
|
+
num = 100000
|
8
|
+
max = 1000
|
9
|
+
threads = []
|
10
|
+
# writers
|
11
|
+
2.times do
|
12
|
+
threads << Thread.new {
|
13
|
+
num.times do
|
14
|
+
key = rand(max)
|
15
|
+
h[key] = key
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
# deleters
|
20
|
+
2.times do
|
21
|
+
threads << Thread.new {
|
22
|
+
num.times do
|
23
|
+
key = rand(max)
|
24
|
+
h.delete(key)
|
25
|
+
end
|
26
|
+
}
|
27
|
+
end
|
28
|
+
# readers
|
29
|
+
2.times do
|
30
|
+
threads << Thread.new {
|
31
|
+
num.times do
|
32
|
+
key = rand(max)
|
33
|
+
h[key]
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
threads.each(&:join)
|
38
|
+
end
|
39
|
+
end
|
data/lib/avl_tree.rb
CHANGED
data/lib/red_black_tree.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'atomic'
|
2
|
+
|
1
3
|
class RedBlackTree
|
2
4
|
include Enumerable
|
3
5
|
|
@@ -7,11 +9,13 @@ class RedBlackTree
|
|
7
9
|
attr_reader :key, :value, :color
|
8
10
|
attr_reader :left, :right
|
9
11
|
|
10
|
-
def initialize(key, value)
|
11
|
-
@key
|
12
|
-
@
|
12
|
+
def initialize(key, value, left, right, color = :RED)
|
13
|
+
@key = key
|
14
|
+
@value = value
|
15
|
+
@left = left
|
16
|
+
@right = right
|
13
17
|
# new node is added as RED
|
14
|
-
@color =
|
18
|
+
@color = color
|
15
19
|
end
|
16
20
|
|
17
21
|
def set_root
|
@@ -67,14 +71,14 @@ class RedBlackTree
|
|
67
71
|
case key <=> @key
|
68
72
|
when -1
|
69
73
|
@left = @left.insert(key, value)
|
70
|
-
if black? and @right.black? and @left.red? and !@left.
|
74
|
+
if black? and @right.black? and @left.red? and !@left.children_color?(:BLACK)
|
71
75
|
ret = rebalance_for_left_insert
|
72
76
|
end
|
73
77
|
when 0
|
74
78
|
@value = value
|
75
79
|
when 1
|
76
80
|
@right = @right.insert(key, value)
|
77
|
-
if black? and @left.black? and @right.red? and !@right.
|
81
|
+
if black? and @left.black? and @right.red? and !@right.children_color?(:BLACK)
|
78
82
|
ret = rebalance_for_right_insert
|
79
83
|
end
|
80
84
|
else
|
@@ -108,7 +112,7 @@ class RedBlackTree
|
|
108
112
|
end
|
109
113
|
when 0
|
110
114
|
deleted = self
|
111
|
-
ret, rebalance =
|
115
|
+
ret, rebalance = delete_node
|
112
116
|
when 1
|
113
117
|
deleted, @right, rebalance = @right.delete(key)
|
114
118
|
if rebalance
|
@@ -156,8 +160,8 @@ class RedBlackTree
|
|
156
160
|
|
157
161
|
protected
|
158
162
|
|
159
|
-
def
|
160
|
-
@right.
|
163
|
+
def children_color?(color)
|
164
|
+
@right.color == @left.color && @right.color == color
|
161
165
|
end
|
162
166
|
|
163
167
|
def color=(color)
|
@@ -176,15 +180,9 @@ class RedBlackTree
|
|
176
180
|
@color, other.color = other.color, @color
|
177
181
|
end
|
178
182
|
|
179
|
-
def node_flip(other)
|
180
|
-
@left, other.left = other.left, @left
|
181
|
-
@right, other.right = other.right, @right
|
182
|
-
color_flip(other)
|
183
|
-
end
|
184
|
-
|
185
183
|
def delete_min
|
186
184
|
if @left.empty?
|
187
|
-
[self, *
|
185
|
+
[self, *delete_node]
|
188
186
|
else
|
189
187
|
ret = self
|
190
188
|
deleted, @left, rebalance = @left.delete_min
|
@@ -201,7 +199,7 @@ class RedBlackTree
|
|
201
199
|
rebalance = false
|
202
200
|
if black?
|
203
201
|
if @right.black?
|
204
|
-
if @right.
|
202
|
+
if @right.children_color?(:BLACK)
|
205
203
|
# make whole sub-tree 1 level lower and ask rebalance
|
206
204
|
@right.color = :RED
|
207
205
|
rebalance = true
|
@@ -217,7 +215,7 @@ class RedBlackTree
|
|
217
215
|
raise 'should not happen' if rebalance
|
218
216
|
end
|
219
217
|
else # red
|
220
|
-
if @right.
|
218
|
+
if @right.children_color?(:BLACK)
|
221
219
|
# make right sub-tree 1 level lower
|
222
220
|
color_flip(@right)
|
223
221
|
else
|
@@ -235,7 +233,7 @@ class RedBlackTree
|
|
235
233
|
rebalance = false
|
236
234
|
if black?
|
237
235
|
if @left.black?
|
238
|
-
if @left.
|
236
|
+
if @left.children_color?(:BLACK)
|
239
237
|
@left.color = :RED
|
240
238
|
rebalance = true
|
241
239
|
else
|
@@ -247,7 +245,7 @@ class RedBlackTree
|
|
247
245
|
raise 'should not happen' if rebalance
|
248
246
|
end
|
249
247
|
else # red
|
250
|
-
if @left.
|
248
|
+
if @left.children_color?(:BLACK)
|
251
249
|
color_flip(@left)
|
252
250
|
else
|
253
251
|
ret = balanced_rotate_right
|
@@ -318,7 +316,7 @@ class RedBlackTree
|
|
318
316
|
# A C a c
|
319
317
|
#
|
320
318
|
def pullup_red
|
321
|
-
if black? and
|
319
|
+
if black? and children_color?(:RED)
|
322
320
|
@left.color = @right.color = :BLACK
|
323
321
|
self.color = :RED
|
324
322
|
end
|
@@ -346,7 +344,7 @@ class RedBlackTree
|
|
346
344
|
rotate_left
|
347
345
|
end
|
348
346
|
|
349
|
-
def
|
347
|
+
def delete_node
|
350
348
|
rebalance = false
|
351
349
|
if @left.empty? and @right.empty?
|
352
350
|
# just remove this node and ask rebalance to the parent
|
@@ -366,8 +364,8 @@ class RedBlackTree
|
|
366
364
|
end
|
367
365
|
else
|
368
366
|
# pick the minimum node from the right sub-tree and replace self with it
|
369
|
-
|
370
|
-
new_root.
|
367
|
+
deleted, @right, rebalance = @right.delete_min
|
368
|
+
new_root = Node.new(deleted.key, deleted.value, @left, @right, @color)
|
371
369
|
if rebalance
|
372
370
|
new_root, rebalance = new_root.rebalance_for_right_delete
|
373
371
|
end
|
@@ -403,7 +401,7 @@ class RedBlackTree
|
|
403
401
|
|
404
402
|
# returns new_root
|
405
403
|
def insert(key, value)
|
406
|
-
Node.new(key, value)
|
404
|
+
Node.new(key, value, self, self)
|
407
405
|
end
|
408
406
|
|
409
407
|
# returns value
|
@@ -441,53 +439,57 @@ class RedBlackTree
|
|
441
439
|
@default_proc = block
|
442
440
|
end
|
443
441
|
|
442
|
+
def root
|
443
|
+
@root
|
444
|
+
end
|
445
|
+
|
444
446
|
def empty?
|
445
|
-
|
447
|
+
root == Node::EMPTY
|
446
448
|
end
|
447
449
|
|
448
450
|
def size
|
449
|
-
|
451
|
+
root.size
|
450
452
|
end
|
451
453
|
alias length size
|
452
454
|
|
453
455
|
def each(&block)
|
454
456
|
if block_given?
|
455
|
-
|
457
|
+
root.each(&block)
|
456
458
|
self
|
457
459
|
else
|
458
|
-
Enumerator.new(
|
460
|
+
Enumerator.new(root)
|
459
461
|
end
|
460
462
|
end
|
461
463
|
alias each_pair each
|
462
464
|
|
463
465
|
def each_key
|
464
466
|
if block_given?
|
465
|
-
|
467
|
+
root.each do |k, v|
|
466
468
|
yield k
|
467
469
|
end
|
468
470
|
self
|
469
471
|
else
|
470
|
-
Enumerator.new(
|
472
|
+
Enumerator.new(root, :each_key)
|
471
473
|
end
|
472
474
|
end
|
473
475
|
|
474
476
|
def each_value
|
475
477
|
if block_given?
|
476
|
-
|
478
|
+
root.each do |k, v|
|
477
479
|
yield v
|
478
480
|
end
|
479
481
|
self
|
480
482
|
else
|
481
|
-
Enumerator.new(
|
483
|
+
Enumerator.new(root, :each_value)
|
482
484
|
end
|
483
485
|
end
|
484
486
|
|
485
487
|
def keys
|
486
|
-
|
488
|
+
root.keys
|
487
489
|
end
|
488
490
|
|
489
491
|
def values
|
490
|
-
|
492
|
+
root.values
|
491
493
|
end
|
492
494
|
|
493
495
|
def clear
|
@@ -502,7 +504,7 @@ class RedBlackTree
|
|
502
504
|
alias insert []=
|
503
505
|
|
504
506
|
def key?(key)
|
505
|
-
|
507
|
+
root.retrieve(key) != Node::UNDEFINED
|
506
508
|
end
|
507
509
|
alias has_key? key?
|
508
510
|
|
@@ -525,13 +527,13 @@ class RedBlackTree
|
|
525
527
|
end
|
526
528
|
|
527
529
|
def dump_tree(io = '')
|
528
|
-
|
530
|
+
root.dump_tree(io)
|
529
531
|
io << $/
|
530
532
|
io
|
531
533
|
end
|
532
534
|
|
533
535
|
def dump_sexp
|
534
|
-
|
536
|
+
root.dump_sexp || ''
|
535
537
|
end
|
536
538
|
|
537
539
|
def to_hash
|
@@ -550,3 +552,297 @@ private
|
|
550
552
|
end
|
551
553
|
end
|
552
554
|
end
|
555
|
+
|
556
|
+
class ConcurrentRedBlackTree < RedBlackTree
|
557
|
+
class ConcurrentNode < Node
|
558
|
+
# direction: ~LEFT == RIGHT, ~RIGHT == LEFT
|
559
|
+
LEFT = -1
|
560
|
+
RIGHT = 0
|
561
|
+
|
562
|
+
# @Overrides
|
563
|
+
def insert(key, value)
|
564
|
+
case key <=> @key
|
565
|
+
when -1
|
566
|
+
dir = LEFT
|
567
|
+
when 0
|
568
|
+
node = new_value(value)
|
569
|
+
when 1
|
570
|
+
dir = RIGHT
|
571
|
+
else
|
572
|
+
raise TypeError, "cannot compare #{key} and #{@key} with <=>"
|
573
|
+
end
|
574
|
+
if dir
|
575
|
+
target = child(dir).insert(key, value)
|
576
|
+
node = new_child(dir, target)
|
577
|
+
if black? and child(~dir).black? and target.red? and !target.children_color?(:BLACK)
|
578
|
+
node = node.rebalance_for_insert(dir)
|
579
|
+
end
|
580
|
+
end
|
581
|
+
node.pullup_red
|
582
|
+
end
|
583
|
+
|
584
|
+
# @Overrides
|
585
|
+
def retrieve(key)
|
586
|
+
case key <=> @key
|
587
|
+
when -1
|
588
|
+
@left.retrieve(key)
|
589
|
+
when 0
|
590
|
+
@value
|
591
|
+
when 1
|
592
|
+
@right.retrieve(key)
|
593
|
+
else
|
594
|
+
nil
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
# @Overrides
|
599
|
+
def delete(key)
|
600
|
+
case key <=> @key
|
601
|
+
when -1
|
602
|
+
dir = LEFT
|
603
|
+
when 0
|
604
|
+
deleted = self
|
605
|
+
node, rebalance = delete_node
|
606
|
+
when 1
|
607
|
+
dir = RIGHT
|
608
|
+
else
|
609
|
+
raise TypeError, "cannot compare #{key} and #{@key} with <=>"
|
610
|
+
end
|
611
|
+
if dir
|
612
|
+
deleted, target, rebalance = child(dir).delete(key)
|
613
|
+
node = new_child(dir, target)
|
614
|
+
if rebalance
|
615
|
+
node, rebalance = node.rebalance_for_delete(dir)
|
616
|
+
end
|
617
|
+
end
|
618
|
+
[deleted, node, rebalance]
|
619
|
+
end
|
620
|
+
|
621
|
+
protected
|
622
|
+
|
623
|
+
def new_children(dir, node, other, color = @color)
|
624
|
+
dir == LEFT ?
|
625
|
+
ConcurrentNode.new(@key, @value, node, other, color) :
|
626
|
+
ConcurrentNode.new(@key, @value, other, node, color)
|
627
|
+
end
|
628
|
+
|
629
|
+
def new_child(dir, node, color = @color)
|
630
|
+
dir == LEFT ?
|
631
|
+
ConcurrentNode.new(@key, @value, node, @right, color) :
|
632
|
+
ConcurrentNode.new(@key, @value, @left, node, color)
|
633
|
+
end
|
634
|
+
|
635
|
+
def new_color(color)
|
636
|
+
ConcurrentNode.new(@key, @value, @left, @right, color)
|
637
|
+
end
|
638
|
+
|
639
|
+
def new_value(value)
|
640
|
+
ConcurrentNode.new(@key, value, @left, @right, @color)
|
641
|
+
end
|
642
|
+
|
643
|
+
def child(dir)
|
644
|
+
dir == LEFT ? @left : @right
|
645
|
+
end
|
646
|
+
|
647
|
+
# @Overrides
|
648
|
+
def delete_min
|
649
|
+
if @left.empty?
|
650
|
+
[self, *delete_node]
|
651
|
+
else
|
652
|
+
deleted, left, rebalance = @left.delete_min
|
653
|
+
node = new_child(LEFT, left)
|
654
|
+
if rebalance
|
655
|
+
node, rebalance = node.rebalance_for_delete(LEFT)
|
656
|
+
end
|
657
|
+
[deleted, node, rebalance]
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
# rebalance when the left/right sub-tree is 1 level lower than the right/left
|
662
|
+
def rebalance_for_delete(dir)
|
663
|
+
target = child(~dir)
|
664
|
+
rebalance = false
|
665
|
+
if black?
|
666
|
+
if target.black?
|
667
|
+
if target.children_color?(:BLACK)
|
668
|
+
# make whole sub-tree 1 level lower and ask rebalance
|
669
|
+
node = new_child(~dir, target.new_color(:RED))
|
670
|
+
rebalance = true
|
671
|
+
else
|
672
|
+
# move 1 black from the right to the left by single/double rotation
|
673
|
+
node = balanced_rotate(dir)
|
674
|
+
end
|
675
|
+
else
|
676
|
+
# flip this sub-tree into another type of 3-children node
|
677
|
+
node = rotate(dir)
|
678
|
+
# try to rebalance in sub-tree
|
679
|
+
target, rebalance = node.child(dir).rebalance_for_delete(dir)
|
680
|
+
raise 'should not happen' if rebalance
|
681
|
+
node = node.new_children(dir, target, node.child(~dir))
|
682
|
+
end
|
683
|
+
else # red
|
684
|
+
if target.children_color?(:BLACK)
|
685
|
+
# make right sub-tree 1 level lower
|
686
|
+
node = new_child(~dir, target.new_color(@color), target.color)
|
687
|
+
else
|
688
|
+
# move 1 black from the right to the left by single/double rotation
|
689
|
+
node = balanced_rotate(dir)
|
690
|
+
end
|
691
|
+
end
|
692
|
+
[node, rebalance]
|
693
|
+
end
|
694
|
+
|
695
|
+
# move 1 black from the right/left to the left/right by single/double rotation
|
696
|
+
def balanced_rotate(dir)
|
697
|
+
target = child(~dir)
|
698
|
+
if target.child(dir).red? and target.child(~dir).black?
|
699
|
+
node = new_child(~dir, target.rotate(~dir))
|
700
|
+
else
|
701
|
+
node = self
|
702
|
+
end
|
703
|
+
node = node.rotate(dir)
|
704
|
+
node.new_children(dir, node.child(dir).new_color(:BLACK), node.child(~dir).new_color(:BLACK))
|
705
|
+
end
|
706
|
+
|
707
|
+
# Right single rotation
|
708
|
+
# (b a (D c E)) where D and E are RED --> (d (B a c) E)
|
709
|
+
#
|
710
|
+
# b d
|
711
|
+
# / \ / \
|
712
|
+
# a D -> B E
|
713
|
+
# / \ / \
|
714
|
+
# c E a c
|
715
|
+
#
|
716
|
+
# Left single rotation
|
717
|
+
# (d (B A c) e) where A and B are RED --> (b A (D c e))
|
718
|
+
#
|
719
|
+
# d b
|
720
|
+
# / \ / \
|
721
|
+
# B e -> A D
|
722
|
+
# / \ / \
|
723
|
+
# A c c e
|
724
|
+
#
|
725
|
+
def rotate(dir)
|
726
|
+
new_root = child(~dir)
|
727
|
+
node = new_child(~dir, new_root.child(dir), new_root.color)
|
728
|
+
new_root.new_children(dir, node, new_root.child(~dir), @color)
|
729
|
+
end
|
730
|
+
|
731
|
+
# Pull up red nodes
|
732
|
+
# (b (A C)) where A and C are RED --> (B (a c))
|
733
|
+
#
|
734
|
+
# b B
|
735
|
+
# / \ -> / \
|
736
|
+
# A C a c
|
737
|
+
#
|
738
|
+
# @Overrides
|
739
|
+
def pullup_red
|
740
|
+
if black? and @left.red? and @right.red?
|
741
|
+
new_children(LEFT, @left.new_color(:BLACK), @right.new_color(:BLACK), :RED)
|
742
|
+
else
|
743
|
+
self
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
# rebalance when the left/right sub-tree is 1 level higher than the right/left
|
748
|
+
# move 1 black from the left to the right by single/double rotation
|
749
|
+
#
|
750
|
+
# precondition: self is black and @left/@right is red
|
751
|
+
def rebalance_for_insert(dir)
|
752
|
+
node = self
|
753
|
+
if child(dir).child(~dir).red?
|
754
|
+
node = new_child(dir, child(dir).rotate(dir))
|
755
|
+
end
|
756
|
+
node.rotate(~dir)
|
757
|
+
end
|
758
|
+
|
759
|
+
private
|
760
|
+
|
761
|
+
# @Overrides
|
762
|
+
def delete_node
|
763
|
+
rebalance = false
|
764
|
+
if @left.empty? and @right.empty?
|
765
|
+
# just remove this node and ask rebalance to the parent
|
766
|
+
new_node = EMPTY_CONCURRENT
|
767
|
+
if black?
|
768
|
+
rebalance = true
|
769
|
+
end
|
770
|
+
elsif @left.empty? or @right.empty?
|
771
|
+
# pick the single children
|
772
|
+
new_node = @left.empty? ? @right : @left
|
773
|
+
if black?
|
774
|
+
# keep the color black
|
775
|
+
raise 'should not happen' unless new_node.red?
|
776
|
+
new_node = new_node.new_color(@color)
|
777
|
+
else
|
778
|
+
# just remove the red node
|
779
|
+
end
|
780
|
+
else
|
781
|
+
# pick the minimum node from the right sub-tree and replace self with it
|
782
|
+
deleted, right, rebalance = @right.delete_min
|
783
|
+
new_node = deleted.new_children(LEFT, @left, right, @color)
|
784
|
+
if rebalance
|
785
|
+
new_node, rebalance = new_node.rebalance_for_delete(RIGHT)
|
786
|
+
end
|
787
|
+
end
|
788
|
+
[new_node, rebalance]
|
789
|
+
end
|
790
|
+
|
791
|
+
class EmptyConcurrentNode < EmptyNode
|
792
|
+
# @Overrides
|
793
|
+
def insert(key, value)
|
794
|
+
ConcurrentNode.new(key, value, self, self)
|
795
|
+
end
|
796
|
+
end
|
797
|
+
EMPTY_CONCURRENT = ConcurrentNode::EmptyConcurrentNode.new.freeze
|
798
|
+
end
|
799
|
+
|
800
|
+
def initialize(default = DEFAULT, &block)
|
801
|
+
super
|
802
|
+
@root = Atomic.new(ConcurrentNode::EMPTY_CONCURRENT)
|
803
|
+
end
|
804
|
+
|
805
|
+
def root
|
806
|
+
@root.get
|
807
|
+
end
|
808
|
+
|
809
|
+
def empty?
|
810
|
+
root == ConcurrentNode::EMPTY_CONCURRENT
|
811
|
+
end
|
812
|
+
|
813
|
+
def clear
|
814
|
+
@root.set(ConcurrentNode::EMPTY_CONCURRENT)
|
815
|
+
end
|
816
|
+
|
817
|
+
def []=(key, value)
|
818
|
+
@root.update { |root|
|
819
|
+
root = root.insert(key, value)
|
820
|
+
root.set_root
|
821
|
+
root.check_height if $DEBUG
|
822
|
+
root
|
823
|
+
}
|
824
|
+
end
|
825
|
+
alias insert []=
|
826
|
+
|
827
|
+
def [](key)
|
828
|
+
value = @root.get.retrieve(key)
|
829
|
+
if value == Node::UNDEFINED
|
830
|
+
default_value
|
831
|
+
else
|
832
|
+
value
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
def delete(key)
|
837
|
+
deleted = nil
|
838
|
+
@root.update { |root|
|
839
|
+
deleted, root, rebalance = root.delete(key)
|
840
|
+
unless root == ConcurrentNode::EMPTY_CONCURRENT
|
841
|
+
root.set_root
|
842
|
+
root.check_height if $DEBUG
|
843
|
+
end
|
844
|
+
root
|
845
|
+
}
|
846
|
+
deleted.value
|
847
|
+
end
|
848
|
+
end
|