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 +5 -2
- data/bench/bench_element_size.rb +11 -10
- data/bench/profile.rb +5 -5
- data/lib/avl_tree.rb +29 -77
- data/lib/red_black_tree.rb +549 -0
- data/test/helper.rb +1 -0
- data/test/test_red_black_tree.rb +595 -0
- metadata +5 -3
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 <<
|
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
|
data/bench/bench_element_size.rb
CHANGED
@@ -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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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/
|
1
|
+
require File.expand_path('../lib/red_black_tree', File.dirname(__FILE__))
|
2
2
|
|
3
3
|
random = Random.new(0)
|
4
4
|
|
5
|
-
TIMES =
|
5
|
+
TIMES = 50000
|
6
6
|
key_size = 10
|
7
7
|
|
8
|
-
h =
|
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
|
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
|
111
|
+
def insert(key, value)
|
112
112
|
case key <=> @key
|
113
113
|
when -1
|
114
|
-
@left = @left.
|
114
|
+
@left = @left.insert(key, value)
|
115
115
|
when 0
|
116
116
|
@value = value
|
117
117
|
when 1
|
118
|
-
@right = @right.
|
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
|
217
|
-
|
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
|
223
|
-
|
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
|
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
|
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
|
-
|
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
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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.
|
358
|
+
@root = @root.insert(key.to_s, value)
|
407
359
|
end
|
408
|
-
alias
|
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
|