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