tree-red_black 0.4.2

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.
@@ -0,0 +1,645 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Tree
3
+ ##
4
+ # Tree::RedBlackNode is a pure-Ruby implementation of a
5
+ # {Red-Black tree}[https://en.wikipedia.org/wiki/Red–black_tree] --
6
+ # i.e., a self-balancing binary search tree with
7
+ # {O(log n)}[https://en.wikipedia.org/wiki/Big-O_notation]
8
+ # search, insert and delete operations. It is appropriate for
9
+ # maintaining an ordered collection where insertion and deletion
10
+ # are desired at arbitrary positions.
11
+ #
12
+ # The implementation differs slightly from the Wikipedia description
13
+ # referenced above. In particular, leaf nodes are +nil+, which
14
+ # affects the details of node deletion.
15
+ #
16
+ # While a Red-Black tree can be constructed from nodes alone, the
17
+ # Tree::RedBlack API provides a cleaner way of working with
18
+ # Red-Black trees. Start there if only using the Red-Black tree as a
19
+ # container.
20
+
21
+ class RedBlackNode
22
+ include Enumerable
23
+
24
+ attr_accessor :left, :right, :key, :parent, :color
25
+
26
+ ##
27
+ # Returns a new node with +key+ parameter set to option +value+.
28
+ # The +color+ option, if given, must be be either <tt>:RED</tt> or
29
+ # <tt>:BLACK</tt>.
30
+ #
31
+ # A Red-Black tree is a collection of nodes arranged as a binary tree.
32
+ # In addition to the binary node attributes `left` and `right`, which
33
+ # reference the left and right sub-trees of a given node, and the
34
+ # attribute `key` which stores a node's data, a Red-Black tree node
35
+ # also has attributes `color` and `parent`.
36
+ #
37
+ # The `color` attribute is used internally to balance the tree after a
38
+ # node is inserted or deleted. The `parent` attribute references a
39
+ # node's parent, or `nil` in the case of the root node of a tree.
40
+
41
+ #
42
+ # === Example
43
+ #
44
+ # require 'tree/red_black'
45
+ #
46
+ # root = Tree::RedBlackNode.new(10)
47
+ # p root.key #=> 10
48
+ # p root.color #=> :RED
49
+ # p root.parent #=> nil
50
+ # p root.left #=> nil
51
+ # p root.right #=> nil
52
+
53
+ def initialize(value = nil, color = :RED)
54
+ raise "color must be :RED or :BLACK" unless [:RED, :BLACK].include?(color)
55
+
56
+ @left = @right = @parent = nil
57
+ @key = value
58
+ @color = color
59
+ end
60
+
61
+ def <=>(other) # :nodoc:
62
+ key <=> other.key
63
+ end
64
+
65
+ def sibling # :nodoc:
66
+ self == parent&.left ? parent&.right : parent&.left
67
+ end
68
+
69
+ def grandparent # :nodoc:
70
+ parent&.parent
71
+ end
72
+
73
+ def parent_sibling # :nodoc:
74
+ parent&.sibling
75
+ end
76
+
77
+ ##
78
+ # Inserts the given +value+ in a tree whose root node is +self+.
79
+ # If the +key+ attribute of the root node is +nil+, then +value+
80
+ # is assigned to +key+. Otherwise, +value+ is used to instantiate
81
+ # a Tree::RedBlackNode, and the node is inserted in the tree; the
82
+ # tree is then re-balanced as needed, and the root of the balanced
83
+ # tree returned.
84
+
85
+ # Since a Red-Black tree maintains an ordered, Enumerable
86
+ # collection, every value inserted must be Comparable with every
87
+ # other value. Methods +each+, +map+, +select+, +find+, +sort+,
88
+ # etc., can be applied to a Red-Black tree's root node to iterate
89
+ # over all nodes in the tree.
90
+ #
91
+ # Each node yielded by enumeration has a +key+ attribute to
92
+ # retrieve the value stored in that node. Method +each+, in
93
+ # particular, is aliased to +in_order+, so that nodes are sorted
94
+ # in ascending order by +key+ value. Nodes can also be traversed
95
+ # by method +pre_order+, e.g., to generate paths in the tree.
96
+ #
97
+ # === Example
98
+ #
99
+ # require 'tree/red_black'
100
+ #
101
+ # root = Tree::RedBlackNode.new
102
+ # p root.key #=> nil
103
+ # root = root.insert_red_black(0)
104
+ # p root.key #=> 0
105
+ # root = root.insert_red_black(1)
106
+ # p root.key #=> 0
107
+ # p root.left #=> nil
108
+ # p root.right.key #=> 1
109
+ # root = root.insert_red_black(2)
110
+ # p root.key #=> 1
111
+ # p root.left.key #=> 0
112
+ # p root.right.key #=> 2
113
+
114
+ def insert_red_black(value, allow_duplicates = true)
115
+ node = allow_duplicates ? insert_key(value) : insert_unique_key(value)
116
+
117
+ return nil if node.nil?
118
+
119
+ node.color_insert
120
+
121
+ while node.parent
122
+ node = node.parent
123
+ end
124
+ node
125
+ end
126
+
127
+ ##
128
+ # Deletes the given +value+ from a tree whose root node is +self+.
129
+ # If the tree has only one remaining node and its +key+ attribute
130
+ # matches +value+, then the remaining node's +key+ attribute is
131
+ # set to +nil+ but the node itself is not removed. Otherwise, the
132
+ # first node found whose +key+ matches +value+ is removed from the
133
+ # tree, and the tree is re-balanced. The root of the balanced tree
134
+ # is returned.
135
+ #
136
+ # === Example
137
+ #
138
+ # require 'tree/red_black'
139
+ #
140
+ # root = [*1..10].reduce(Tree::RedBlackNode.new) do |acc, v|
141
+ # acc.insert_red_black(v)
142
+ # end
143
+ # root = [*4..8].reduce(root) do |acc, v|
144
+ # acc.delete_red_black(v)
145
+ # end
146
+ # root.map(&:key) #=> [1, 2, 3, 9, 10]
147
+
148
+ def delete_red_black(value)
149
+ if key.nil?
150
+ nil
151
+ elsif value > key
152
+ right ? right.delete_red_black(value) : nil
153
+ elsif value < key
154
+ left ? left.delete_red_black(value) : nil
155
+ else
156
+ if left && right
157
+ node = right.min
158
+ @key = node.key
159
+ node.substitute_with_child
160
+ else
161
+ substitute_with_child
162
+ end
163
+ end
164
+ end
165
+
166
+ ##
167
+ # Returns a node whose +key+ matches +value+ by binary search. If no
168
+ # match is found, calls non-nil +ifnone+, otherwise returns +nil+.
169
+ #
170
+ # === Example
171
+ # require 'tree/red_black'
172
+ #
173
+ # shuffled_values = [*1..10].shuffle
174
+ # root = shuffled_values.reduce(Tree::RedBlackNode.new) do |acc, v|
175
+ # acc.insert_red_black(v)
176
+ # end
177
+ # root.search(7) #=> <Tree::RedBlackNode:0x00..., @key=7, ...>
178
+
179
+ def search(value, ifnone = nil)
180
+ if key.nil?
181
+ ifnone && ifnone.call
182
+ elsif value > key
183
+ right ? right.search(value, ifnone) : ifnone && ifnone.call
184
+ elsif value < key
185
+ left ? left.search(value, ifnone) : ifnone && ifnone.call
186
+ else
187
+ self
188
+ end
189
+ end
190
+
191
+ ##
192
+ # Returns a node satisfying a criterion defined in +block+ by
193
+ # binary search.
194
+ #
195
+ # If +block+ evaluates to +true+ or +false+, returns the first node
196
+ # for which the +block+ evaluates to +true+. In this case, the
197
+ # criterion is expected to return +false+ for nodes preceding
198
+ # the matching node and +true+ for subsequent nodes.
199
+ #
200
+ # === Example
201
+ # require 'tree/red_black'
202
+ #
203
+ # shuffled_values = [*1..10].shuffle
204
+ # root = shuffled_values.reduce(Tree::RedBlackNode.new) do |acc, v|
205
+ # acc.insert_red_black(v)
206
+ # end
207
+ # root.bsearch { |node| node.key >= 7 }
208
+ # #=> <Tree::RedBlackNode:0x00... @key=7 ...>
209
+ #
210
+ # If +block+ evaluates to <tt><0</tt>, +0+ or <tt>>0</tt>, returns
211
+ # first node for which +block+ evaluates to +0+. Otherwise returns
212
+ # +nil+. In this case, the criterion is expected to return
213
+ # <tt><0</tt> for nodes preceding the matching node, +0+ for some
214
+ # subsequent nodes and <tt>>0</tt> for nodes beyond that.
215
+ #
216
+ # === Example
217
+ # require 'tree/red_black'
218
+ #
219
+ # shuffled_values = [*1..10].shuffle
220
+ # root = shuffled_values.reduce(Tree::RedBlackNode.new) do |acc, v|
221
+ # acc.insert_red_black(v)
222
+ # end
223
+ # root.bsearch { |node| 7 <=> node.key }
224
+ # #=> <Tree::RedBlackNode:0x00... @key=7 ...>
225
+ #
226
+ # If +block++ is not given, returns an enumerator.
227
+
228
+ def bsearch(&block)
229
+ return enum_for(:bsearch) unless block_given?
230
+
231
+ return nil if key.nil?
232
+
233
+ result = block.call(self)
234
+ case result
235
+ when Integer
236
+ if result > 0
237
+ right ? right.bsearch(&block) : nil
238
+ elsif result < 0
239
+ left ? left.bsearch(&block) : nil
240
+ else
241
+ self
242
+ end
243
+ when TrueClass, FalseClass
244
+ if result
245
+ left ? (node = left.bsearch(&block); node ? node : self) : self
246
+ else
247
+ right ? right.bsearch(&block) : nil
248
+ end
249
+ else
250
+ nil
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Returns the node whose +key+ is a minimum in the sub-tree with
256
+ # root +self+.
257
+ #
258
+ # === Example
259
+ # require 'tree/red_black'
260
+ #
261
+ # root = [*0..10].reduce(Tree::RedBlackNode.new) do |acc, v|
262
+ # acc.insert_red_black(v)
263
+ # end
264
+ # root #=> <Tree::RedBlackNode:0x00..., @key=4, ...>
265
+ # root.min #=> <Tree::RedBlackNode:0x00..., @key=0, ...>
266
+ # root.right #=> <Tree::RedBlackNode:0x00..., @key=6, ...>
267
+ # root.right.min #=> <Tree::RedBlackNode:0x00..., @key=5, ...>
268
+
269
+ def min
270
+ node = self
271
+ while node.left
272
+ node = node.left
273
+ end
274
+ node
275
+ end
276
+
277
+ ##
278
+ # Returns the node whose +key+ is a maximum in the sub-tree with
279
+ # root +self+.
280
+ #
281
+ # === Example
282
+ # require 'tree/red_black'
283
+ #
284
+ # root = [*0..10].reduce(Tree::RedBlackNode.new) do |acc, v|
285
+ # acc.insert_red_black(v)
286
+ # end
287
+ # root #=> <Tree::RedBlackNode:0x00..., @key=4, ...>
288
+ # root.max #=> <Tree::RedBlackNode:0x00..., @key=10, ...>
289
+ # root.left #=> <Tree::RedBlackNode:0x00..., @key=2, ...>
290
+ # root.left.max #=> <Tree::RedBlackNode:0x00..., @key=3, ...>
291
+
292
+ def max
293
+ node = self
294
+ while node.right
295
+ node = node.right
296
+ end
297
+ node
298
+ end
299
+
300
+ ##
301
+ # Returns the node preceding +self+, or +nil+ if no predecessor
302
+ # exists. If duplicate keys are allowed, it's possible that
303
+ # <tt>pred.key == key</tt>.
304
+ #
305
+ # === Example
306
+ # require 'tree/red_black'
307
+ #
308
+ # root = [*1..10].reduce(Tree::RedBlackNode.new) do |acc, v|
309
+ # acc.insert_red_black(v)
310
+ # end
311
+ # root.right.right.key #=> 8
312
+ # root.right.right.pred.key #=> 7
313
+
314
+ def pred
315
+ return left.max if left
316
+
317
+ node = parent
318
+ while node && node.key > key
319
+ node = node.parent
320
+ end
321
+ node
322
+ end
323
+
324
+ ##
325
+ # Returns the node succeeding +self+, or +nil+ if no successor
326
+ # exists. If duplicate keys are allowed, it's possible that
327
+ # <tt>succ.key == key</tt>.
328
+ #
329
+ # === Example
330
+ # require 'tree/red_black'
331
+ #
332
+ # root = [*1..10].reduce(Tree::RedBlackNode.new) do |acc, v|
333
+ # acc.insert_red_black(v)
334
+ # end
335
+ # root.right.right.key #=> 8
336
+ # root.right.right.succ.key #=> 9
337
+
338
+ def succ
339
+ return right.min if right
340
+
341
+ node = parent
342
+ while node && node.key < key
343
+ node = node.parent
344
+ end
345
+ node
346
+ end
347
+
348
+ ##
349
+ # Returns an enumerator for nodes in the tree with root +self+ by
350
+ # pre-order traversal.
351
+ #
352
+ # === Example
353
+ # require 'tree/red_black'
354
+ #
355
+ # root = [*1..10].reduce(Tree::RedBlackNode.new) do |acc, v|
356
+ # acc.insert_red_black(v)
357
+ # end
358
+ # root.pre_order.map(&:key) #=> [4, 2, 1, 3, 6, 5, 8, 7, 9, 10]
359
+
360
+ def pre_order(&block)
361
+ return enum_for(:pre_order) unless block_given?
362
+
363
+ yield self
364
+ left.pre_order(&block) if left
365
+ right.pre_order(&block) if right
366
+ end
367
+
368
+ ##
369
+ # Returns an enumerator for nodes in the tree with root +self+ by
370
+ # in-order traversal.
371
+ #
372
+ # === Example
373
+ # require 'tree/red_black'
374
+ #
375
+ # shuffled_values = [*1..10].shuffle
376
+ # root = shuffled_values.reduce(Tree::RedBlackNode.new) do |acc, v|
377
+ # acc.insert_red_black(v)
378
+ # end
379
+ # root.in_order.map(&:key) #=> [1, 2, ..., 10]
380
+
381
+ def in_order(&block)
382
+ return enum_for(:in_order) unless block_given?
383
+
384
+ left.in_order(&block) if left
385
+ yield self
386
+ right.in_order(&block) if right
387
+ end
388
+
389
+ ##
390
+ # Returns a deep copy of the tree with root +self+, provided that
391
+ # the +dup+ method for the +key+ attribute of a node is also a
392
+ # deep copy.
393
+ #
394
+ # === Example
395
+ #
396
+ # require 'tree/red_black'
397
+ #
398
+ # root = Tree::RedBlackNode.new({a: 1, b: 2})
399
+ # root_copy = root.dup
400
+ # p root.key #=> {:a=>1, :b=>2}
401
+ # p root.key.delete(:a) #=> 1
402
+ # p root.key #=> {:b=>2}
403
+ # p root_copy.key #=> {:a=>1, :b=>2}
404
+
405
+ def dup
406
+ copy = RedBlackNode.new(key.dup, color)
407
+ if left
408
+ copy.left = left.dup
409
+ copy.left.parent = copy
410
+ end
411
+ if right
412
+ copy.right = right.dup
413
+ copy.right.parent = copy
414
+ end
415
+ copy
416
+ end
417
+
418
+ def insert_key(value) # :nodoc:
419
+ if key.nil?
420
+ @key = value
421
+ self
422
+ elsif value >= key
423
+ if right
424
+ right.insert_key(value)
425
+ else
426
+ @right = RedBlackNode.new(value)
427
+ @right.parent = self
428
+ right
429
+ end
430
+ else
431
+ if left
432
+ left.insert_key(value)
433
+ else
434
+ @left = RedBlackNode.new(value)
435
+ @left.parent = self
436
+ left
437
+ end
438
+ end
439
+ end
440
+
441
+ def insert_unique_key(value) # :nodoc:
442
+ if key.nil?
443
+ @key = value
444
+ self
445
+ elsif value > key
446
+ if right
447
+ right.insert_unique_key(value)
448
+ else
449
+ @right = RedBlackNode.new(value)
450
+ @right.parent = self
451
+ right
452
+ end
453
+ elsif value < key
454
+ if left
455
+ left.insert_unique_key(value)
456
+ else
457
+ @left = RedBlackNode.new(value)
458
+ @left.parent = self
459
+ left
460
+ end
461
+ else
462
+ nil
463
+ end
464
+ end
465
+
466
+ def color_insert # :nodoc:
467
+ if parent.nil?
468
+ @color = :BLACK
469
+ elsif parent.color == :BLACK
470
+ return
471
+ elsif parent_sibling&.color == :RED
472
+ parent.color = parent_sibling.color = :BLACK
473
+ grandparent.color = :RED
474
+ grandparent.color_insert
475
+ else
476
+ node = if self == parent.right && parent == grandparent&.left
477
+ parent.rotate_left.left
478
+ elsif self == parent.left && parent == grandparent&.right
479
+ parent.rotate_right.right
480
+ else
481
+ self
482
+ end
483
+ node.parent.color = :BLACK
484
+ if node.grandparent
485
+ node.grandparent.color = :RED
486
+ if node == node.parent.left
487
+ node.grandparent.rotate_right
488
+ else
489
+ node.grandparent.rotate_left
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ def substitute_with_child # :nodoc:
496
+ if (child = right.nil? ? left : right)
497
+ child.parent = parent
498
+ child.color = :BLACK if color == :BLACK
499
+ end
500
+
501
+ if self == parent&.left
502
+ parent.left = child
503
+ parent.color_delete_left if (color == :BLACK && child.nil?)
504
+ elsif self == parent&.right
505
+ parent.right = child
506
+ parent.color_delete_right if (color == :BLACK && child.nil?)
507
+ end
508
+
509
+ node = parent ? parent : child
510
+ if node.nil?
511
+ @key = nil
512
+ self
513
+ else
514
+ while node.parent
515
+ node = node.parent
516
+ end
517
+ node
518
+ end
519
+ end
520
+
521
+ def color_delete_right # :nodoc:
522
+ child_sibling = left
523
+
524
+ if child_sibling.color == :RED
525
+ @color = :RED
526
+ child_sibling.color = :BLACK
527
+ rotate_right
528
+ child_sibling = left
529
+ end
530
+
531
+ if (color == :BLACK &&
532
+ child_sibling.color == :BLACK &&
533
+ (child_sibling.left.nil? || child_sibling.left.color == :BLACK) &&
534
+ (child_sibling.right.nil? || child_sibling.right.color == :BLACK))
535
+ child_sibling.color = :RED
536
+ if self == parent&.left
537
+ parent.color_delete_left
538
+ elsif self == parent&.right
539
+ parent.color_delete_right
540
+ end
541
+ elsif (color == :RED &&
542
+ child_sibling.color == :BLACK &&
543
+ (child_sibling.left.nil? || child_sibling.left.color == :BLACK) &&
544
+ (child_sibling.right.nil? || child_sibling.right.color == :BLACK))
545
+ child_sibling.color = :RED
546
+ @color = :BLACK
547
+ else
548
+ if child_sibling.color == :BLACK
549
+ if (child_sibling.right&.color == :RED &&
550
+ (child_sibling.left.nil? || child_sibling.left&.color == :BLACK))
551
+ child_sibling.color = :RED
552
+ child_sibling.right.color = :BLACK
553
+ child_sibling.rotate_left
554
+ child_sibling = left
555
+ end
556
+ end
557
+
558
+ child_sibling.color = color
559
+ @color = :BLACK
560
+ child_sibling.left.color = :BLACK # if child_sibling.left
561
+ rotate_right
562
+ end
563
+ end
564
+
565
+ def color_delete_left # :nodoc:
566
+ child_sibling = right
567
+
568
+ if child_sibling.color == :RED
569
+ @color = :RED
570
+ child_sibling.color = :BLACK
571
+ rotate_left
572
+ child_sibling = right
573
+ end
574
+
575
+ if (color == :BLACK &&
576
+ child_sibling.color == :BLACK &&
577
+ (child_sibling.left.nil? || child_sibling.left.color == :BLACK) &&
578
+ (child_sibling.right.nil? || child_sibling.right.color == :BLACK))
579
+ child_sibling.color = :RED
580
+ if self == parent&.left
581
+ parent.color_delete_left
582
+ elsif self == parent&.right
583
+ parent.color_delete_right
584
+ end
585
+ elsif (color == :RED &&
586
+ child_sibling.color == :BLACK &&
587
+ (child_sibling.left.nil? || child_sibling.left.color == :BLACK) &&
588
+ (child_sibling.right.nil? || child_sibling.right.color == :BLACK))
589
+ child_sibling.color = :RED
590
+ @color = :BLACK
591
+ else
592
+ if child_sibling.color == :BLACK
593
+ if ((child_sibling.right.nil? || child_sibling.right.color == :BLACK) &&
594
+ child_sibling.left&.color == :RED)
595
+ child_sibling.color = :RED
596
+ child_sibling.left.color = :BLACK
597
+ child_sibling.rotate_right
598
+ child_sibling = right
599
+ end
600
+ end
601
+
602
+ child_sibling.color = color
603
+ @color = :BLACK
604
+ child_sibling.right.color = :BLACK # if child_sibling.right
605
+ rotate_left
606
+ end
607
+ end
608
+
609
+ def rotate_right # :nodoc:
610
+ return self if left.nil?
611
+
612
+ root = left
613
+ root.right.parent = self unless (@left = root.right).nil?
614
+ if (root.parent = parent)
615
+ if self == parent.left
616
+ @parent.left = root
617
+ else
618
+ @parent.right = root
619
+ end
620
+ end
621
+ root.right = self
622
+ @parent = root
623
+ root
624
+ end
625
+
626
+ def rotate_left # :nodoc:
627
+ return self if right.nil?
628
+
629
+ root = right
630
+ root.left.parent = self unless (@right = root.left).nil?
631
+ if (root.parent = parent)
632
+ if self == parent.left
633
+ @parent.left = root
634
+ else
635
+ @parent.right = root
636
+ end
637
+ end
638
+ root.left = self
639
+ @parent = root
640
+ root
641
+ end
642
+
643
+ alias each in_order
644
+ end
645
+ end