rbtree-ruby 0.3.3 → 0.3.5
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 +4 -4
- data/CHANGELOG.md +26 -0
- data/README.ja.md +10 -4
- data/README.md +10 -4
- data/lib/rbtree/version.rb +1 -1
- data/lib/rbtree.rb +319 -289
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d89cd253c189b3342f30e2a4df3a293561e2353af6592d86f25fcea5eeb76433
|
|
4
|
+
data.tar.gz: 9af04bff3851e7806fc637c2fc0ad583decf8e7c020edf841220a2b90fe8aaf4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a9708ffb8b421f883953c68582c607b9333e671542e21c0f3e0927838d2bb68202142150c11dd11541ea79fa1fcafc6c3195456c38d4b0db6b3897272d205835
|
|
7
|
+
data.tar.gz: 601190e616b7b478cc6ceabe682e9d43ef169871753667fe61e326f65daceaa60441122a550f8458f1d4a7a794a032ac0add88f326956db903854ee96e34b613
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.5] - 2026-01-26
|
|
9
|
+
|
|
10
|
+
### Optimized
|
|
11
|
+
- **O(1) Max Access**: Added cached `@max_node` to ensure `max` and `pop` operations run in constant time.
|
|
12
|
+
- **Traversal Engine Rewrite**: Optimized all range queries (`lt`, `gt`, `between`, `each`) with a unified traversal engine featuring $O(1)$ range rejection and bound optimization.
|
|
13
|
+
- **Fast-path Startup**: Range queries now leverage the hybrid Hash index to jump directly to target nodes (even for exclusive bounds), eliminating $O(\log N)$ tree descents when keys exist.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- **Defensive Assertion**: Added an explicit check to `find_nearest_node` to ensure only `Numeric` keys are accepted, formalizing a pre-existing design constraint.
|
|
17
|
+
- **Internal Refactoring**: Unified the insertion logic between `RBTree` and `MultiRBTree` into a single internal method to improve maintainability and ensure consistent optimization.
|
|
18
|
+
|
|
19
|
+
## [0.3.4] - 2026-01-25
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **Range-based Lookup**: Support for passing `Range` objects to the bracket operator `[]`.
|
|
23
|
+
- `tree[2..4]` -> `between(2, 4)`
|
|
24
|
+
- `tree[2...4]` -> `between(2, 4, include_max: false)`
|
|
25
|
+
- `tree[5..]` -> `gte(5)`
|
|
26
|
+
- `tree[..10]` -> `lte(10)`
|
|
27
|
+
- `tree[...10]` -> `lt(10)`
|
|
28
|
+
- Returns an `Enumerator` of `[key, value]` pairs.
|
|
29
|
+
- *Note: `gt` (greater than exclusive) is not covered by standard Ruby Range syntax and remains available via the `gt` method.*
|
|
30
|
+
|
|
8
31
|
## [0.3.3] - 2026-01-23
|
|
9
32
|
|
|
10
33
|
### Optimized
|
|
@@ -207,6 +230,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
207
230
|
- ASCII diagrams for tree rotation operations
|
|
208
231
|
- MIT License (Copyright © 2026 Masahito Suzuki)
|
|
209
232
|
|
|
233
|
+
[0.3.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.5
|
|
234
|
+
[0.3.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.4
|
|
235
|
+
[0.3.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.3
|
|
210
236
|
[0.3.2]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.2
|
|
211
237
|
[0.3.1]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.1
|
|
212
238
|
[0.3.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.0
|
data/README.ja.md
CHANGED
|
@@ -80,9 +80,16 @@ tree.max # => [20, "twenty"]
|
|
|
80
80
|
|
|
81
81
|
# 範囲クエリ(Enumeratorを返す、配列には.to_aを使用)
|
|
82
82
|
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
83
|
-
tree.gte(10).
|
|
83
|
+
tree.gte(10).each { |k, v| puts k } # ブロック渡しでのループ
|
|
84
84
|
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
85
85
|
|
|
86
|
+
# []でのRangeオブジェクトの利用 (v0.3.4+)
|
|
87
|
+
tree[..10].to_a # lte(10)相当
|
|
88
|
+
tree[2..10].each { |k, v| ... } # Rangeでのループ
|
|
89
|
+
tree[2...10].to_a # between(2, 10, include_max: false)相当
|
|
90
|
+
tree[10..].to_a # gte(10)相当
|
|
91
|
+
tree[2..10, reverse: true].to_a # オプション指定可能
|
|
92
|
+
|
|
86
93
|
# shiftとpop
|
|
87
94
|
tree.shift # => [1, "one"] (最小値を削除)
|
|
88
95
|
tree.pop # => [20, "twenty"] (最大値を削除)
|
|
@@ -232,10 +239,9 @@ tree.max(last: true) # => [2, "b"] (最大キーの最後の値)
|
|
|
232
239
|
- `delete(key)` - O(log n)
|
|
233
240
|
- `value(key)` / `[]` - **O(1)** (内部ハッシュインデックスによる超高速アクセス)
|
|
234
241
|
- `has_key?` - **O(1)** (内部ハッシュインデックスによる超高速チェック)
|
|
235
|
-
- `min` - **O(1)**
|
|
236
|
-
- `max` - O(log n)
|
|
242
|
+
- `min` / `max` - **O(1)**
|
|
237
243
|
- `shift` / `pop` - O(log n)
|
|
238
|
-
- `prev` / `succ` - O(log n)、O(1)
|
|
244
|
+
- `prev` / `succ` - O(log n)、O(1)ハッシュチェックと高速な走査開始により改善
|
|
239
245
|
|
|
240
246
|
全要素のイテレーションはO(n)時間。
|
|
241
247
|
|
data/README.md
CHANGED
|
@@ -80,9 +80,16 @@ tree.max # => [20, "twenty"]
|
|
|
80
80
|
|
|
81
81
|
# Range queries (return Enumerator, use .to_a for Array)
|
|
82
82
|
tree.lt(10).to_a # => [[1, "one"], [2, "two"], [3, "three"]]
|
|
83
|
-
tree.gte(10).
|
|
83
|
+
tree.gte(10).each { |k, v| puts k } # Block iteration
|
|
84
84
|
tree.between(2, 10).to_a # => [[2, "two"], [3, "three"], [10, "ten"]]
|
|
85
85
|
|
|
86
|
+
# Range objects in [] (v0.3.4+)
|
|
87
|
+
tree[..10].to_a # lte(10)
|
|
88
|
+
tree[2..10].each { |k, v| ... } # Block iteration on Range
|
|
89
|
+
tree[2...10].to_a # between(2, 10, include_max: false)
|
|
90
|
+
tree[10..].to_a # gte(10)
|
|
91
|
+
tree[2..10, reverse: true].to_a # with options
|
|
92
|
+
|
|
86
93
|
# Shift and pop
|
|
87
94
|
tree.shift # => [1, "one"] (removes minimum)
|
|
88
95
|
tree.pop # => [20, "twenty"] (removes maximum)
|
|
@@ -232,10 +239,9 @@ All major operations run in **O(log n)** time:
|
|
|
232
239
|
- `delete(key)` - O(log n)
|
|
233
240
|
- `value(key)` / `[]` - **O(1)** (hybrid hash index)
|
|
234
241
|
- `has_key?` - **O(1)** (hybrid hash index)
|
|
235
|
-
- `min` - **O(1)**
|
|
236
|
-
- `max` - O(log n)
|
|
242
|
+
- `min` / `max` - **O(1)**
|
|
237
243
|
- `shift` / `pop` - O(log n)
|
|
238
|
-
- `prev` / `succ` - O(log n) with O(1) hash check
|
|
244
|
+
- `prev` / `succ` - O(log n) with O(1) hash check and faster startup
|
|
239
245
|
|
|
240
246
|
Iteration over all elements takes O(n) time.
|
|
241
247
|
|
data/lib/rbtree/version.rb
CHANGED
data/lib/rbtree.rb
CHANGED
|
@@ -99,6 +99,7 @@ class RBTree
|
|
|
99
99
|
@nil_node.right = @nil_node
|
|
100
100
|
@root = @nil_node
|
|
101
101
|
@min_node = @nil_node
|
|
102
|
+
@max_node = @nil_node
|
|
102
103
|
@hash_index = {} # Hash index for O(1) key lookup
|
|
103
104
|
@node_allocator = node_allocator
|
|
104
105
|
@key_count = 0
|
|
@@ -191,11 +192,35 @@ class RBTree
|
|
|
191
192
|
# @example
|
|
192
193
|
# tree = RBTree.new({1 => 'one', 2 => 'two'})
|
|
193
194
|
# tree.get(1) # => "one"
|
|
194
|
-
# tree[2] # => "two"
|
|
195
|
-
# tree[3] # => nil
|
|
196
195
|
def value(key) = @hash_index[key]&.value
|
|
197
196
|
alias :get :value
|
|
198
|
-
|
|
197
|
+
|
|
198
|
+
# Retrieves a value associated with the given key, or a range of entries if a Range is provided.
|
|
199
|
+
#
|
|
200
|
+
# @param key_or_range [Object, Range] the key to look up or a Range for query
|
|
201
|
+
# @param ... [Hash] additional options to pass to the respective lookup method
|
|
202
|
+
# @return [Object, Enumerator, nil]
|
|
203
|
+
# - If a key is provided: the associated value, or nil if not found
|
|
204
|
+
# - If a Range is provided: an Enumerator yielding [key, value] pairs
|
|
205
|
+
# @example Single key lookup
|
|
206
|
+
# tree[2] # => "two"
|
|
207
|
+
# @example Range lookup
|
|
208
|
+
# tree[2..4].to_a # => [[2, "two"], [3, "three"], [4, "four"]]
|
|
209
|
+
# tree[...3].to_a # => [[1, "one"], [2, "two"]]
|
|
210
|
+
def [](key_or_range, **)
|
|
211
|
+
return value(key_or_range, **) if !key_or_range.is_a?(Range)
|
|
212
|
+
|
|
213
|
+
r = key_or_range
|
|
214
|
+
r.begin ? (
|
|
215
|
+
r.end ?
|
|
216
|
+
between(r.begin, r.end, include_max: !r.exclude_end?, **) :
|
|
217
|
+
gte(r.begin, **)
|
|
218
|
+
) : (
|
|
219
|
+
r.end ?
|
|
220
|
+
(r.exclude_end? ? lt(r.end, **) : lte(r.end, **)) :
|
|
221
|
+
each(**)
|
|
222
|
+
)
|
|
223
|
+
end
|
|
199
224
|
|
|
200
225
|
# Returns the key with the key closest to the given key.
|
|
201
226
|
#
|
|
@@ -394,7 +419,7 @@ class RBTree
|
|
|
394
419
|
# tree.pop # => [3, "three"]
|
|
395
420
|
# tree.pop # => [2, "two"]
|
|
396
421
|
def pop
|
|
397
|
-
return nil unless (n =
|
|
422
|
+
return nil unless (n = @max_node) != @nil_node
|
|
398
423
|
pair = n.pair
|
|
399
424
|
delete(n.key)
|
|
400
425
|
pair
|
|
@@ -404,7 +429,7 @@ class RBTree
|
|
|
404
429
|
#
|
|
405
430
|
# @return [RBTree] self
|
|
406
431
|
def clear
|
|
407
|
-
@root = @min_node = @nil_node
|
|
432
|
+
@root = @min_node = @max_node = @nil_node
|
|
408
433
|
@hash_index.clear
|
|
409
434
|
@key_count = 0
|
|
410
435
|
self
|
|
@@ -460,11 +485,7 @@ class RBTree
|
|
|
460
485
|
# end
|
|
461
486
|
def each(reverse: false, safe: false, &block)
|
|
462
487
|
return enum_for(__method__, reverse: reverse, safe: safe) { size } unless block_given?
|
|
463
|
-
|
|
464
|
-
traverse_all_desc(@root, safe: safe, &block)
|
|
465
|
-
else
|
|
466
|
-
traverse_all_asc(@root, safe: safe, &block)
|
|
467
|
-
end
|
|
488
|
+
traverse_range(reverse, nil, nil, false, false, safe: safe, &block)
|
|
468
489
|
self
|
|
469
490
|
end
|
|
470
491
|
|
|
@@ -506,11 +527,7 @@ class RBTree
|
|
|
506
527
|
# tree.lt(3, safe: true) { |k, _| tree.delete(k) if k.even? } # safe to delete
|
|
507
528
|
def lt(key, reverse: false, safe: false, &block)
|
|
508
529
|
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
509
|
-
|
|
510
|
-
traverse_lt_desc(@root, key, safe: safe, &block)
|
|
511
|
-
else
|
|
512
|
-
traverse_lt_asc(@root, key, safe: safe, &block)
|
|
513
|
-
end
|
|
530
|
+
traverse_range(reverse, nil, key, false, false, safe: safe, &block)
|
|
514
531
|
self
|
|
515
532
|
end
|
|
516
533
|
|
|
@@ -527,11 +544,7 @@ class RBTree
|
|
|
527
544
|
# tree.lte(3, reverse: true).first # => [3, "three"]
|
|
528
545
|
def lte(key, reverse: false, safe: false, &block)
|
|
529
546
|
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
530
|
-
|
|
531
|
-
traverse_lte_desc(@root, key, safe: safe, &block)
|
|
532
|
-
else
|
|
533
|
-
traverse_lte_asc(@root, key, safe: safe, &block)
|
|
534
|
-
end
|
|
547
|
+
traverse_range(reverse, nil, key, false, true, safe: safe, &block)
|
|
535
548
|
self
|
|
536
549
|
end
|
|
537
550
|
|
|
@@ -548,11 +561,7 @@ class RBTree
|
|
|
548
561
|
# tree.gt(2, reverse: true).first # => [4, "four"]
|
|
549
562
|
def gt(key, reverse: false, safe: false, &block)
|
|
550
563
|
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
551
|
-
|
|
552
|
-
traverse_gt_desc(@root, key, safe: safe, &block)
|
|
553
|
-
else
|
|
554
|
-
traverse_gt_asc(@root, key, safe: safe, &block)
|
|
555
|
-
end
|
|
564
|
+
traverse_range(reverse, key, nil, false, false, safe: safe, &block)
|
|
556
565
|
self
|
|
557
566
|
end
|
|
558
567
|
|
|
@@ -569,11 +578,7 @@ class RBTree
|
|
|
569
578
|
# tree.gte(2, reverse: true).first # => [4, "four"]
|
|
570
579
|
def gte(key, reverse: false, safe: false, &block)
|
|
571
580
|
return enum_for(__method__, key, reverse: reverse, safe: safe) unless block_given?
|
|
572
|
-
|
|
573
|
-
traverse_gte_desc(@root, key, safe: safe, &block)
|
|
574
|
-
else
|
|
575
|
-
traverse_gte_asc(@root, key, safe: safe, &block)
|
|
576
|
-
end
|
|
581
|
+
traverse_range(reverse, key, nil, true, false, safe: safe, &block)
|
|
577
582
|
self
|
|
578
583
|
end
|
|
579
584
|
|
|
@@ -593,11 +598,7 @@ class RBTree
|
|
|
593
598
|
# tree.between(2, 4, reverse: true).first # => [4, "four"]
|
|
594
599
|
def between(min, max, include_min: true, include_max: true, reverse: false, safe: false, &block)
|
|
595
600
|
return enum_for(__method__, min, max, include_min: include_min, include_max: include_max, reverse: reverse, safe: safe) unless block_given?
|
|
596
|
-
|
|
597
|
-
traverse_between_desc(@root, min, max, include_min, include_max, safe: safe, &block)
|
|
598
|
-
else
|
|
599
|
-
traverse_between_asc(@root, min, max, include_min, include_max, safe: safe, &block)
|
|
600
|
-
end
|
|
601
|
+
traverse_range(reverse, min, max, include_min, include_max, safe: safe, &block)
|
|
601
602
|
self
|
|
602
603
|
end
|
|
603
604
|
|
|
@@ -631,9 +632,8 @@ class RBTree
|
|
|
631
632
|
# @!visibility private
|
|
632
633
|
private
|
|
633
634
|
|
|
634
|
-
def min_node = (
|
|
635
|
-
|
|
636
|
-
def max_node = ((n = rightmost(@root)) == @nil_node) ? nil : n
|
|
635
|
+
def min_node = (@min_node == @nil_node) ? nil : @min_node
|
|
636
|
+
def max_node = (@max_node == @nil_node) ? nil : @max_node
|
|
637
637
|
|
|
638
638
|
# Inserts a single key-value pair.
|
|
639
639
|
#
|
|
@@ -642,27 +642,51 @@ class RBTree
|
|
|
642
642
|
# @param overwrite [Boolean] whether to overwrite existing keys (default: true)
|
|
643
643
|
# @return [Boolean, nil] true if inserted/updated, nil if key exists and overwrite is false
|
|
644
644
|
def insert_entry(key, value, overwrite: true)
|
|
645
|
+
insert_entry_generic(key) do |node, is_new|
|
|
646
|
+
if is_new
|
|
647
|
+
value
|
|
648
|
+
else
|
|
649
|
+
if overwrite
|
|
650
|
+
node.value = value
|
|
651
|
+
true
|
|
652
|
+
else
|
|
653
|
+
nil
|
|
654
|
+
end
|
|
655
|
+
end
|
|
656
|
+
end
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# Generic entry insertion logic shared between RBTree and MultiRBTree.
|
|
660
|
+
#
|
|
661
|
+
# @param key [Object] the key to insert
|
|
662
|
+
# @yield [node, is_new] yields the existing node (if any) and whether it's a new insertion
|
|
663
|
+
# @yieldparam node [Node, nil] the existing node or nil
|
|
664
|
+
# @yieldparam is_new [Boolean] true if no node with the key exists
|
|
665
|
+
# @yieldreturn [Object]
|
|
666
|
+
# - if is_new: the initial value for the new node
|
|
667
|
+
# - if !is_new: the value to return from insert_entry
|
|
668
|
+
def insert_entry_generic(key)
|
|
645
669
|
if (node = @hash_index[key])
|
|
646
|
-
return
|
|
647
|
-
node.value = value
|
|
648
|
-
return true
|
|
670
|
+
return yield(node, false)
|
|
649
671
|
end
|
|
672
|
+
|
|
650
673
|
y = @nil_node
|
|
651
674
|
x = @root
|
|
652
675
|
while x != @nil_node
|
|
653
676
|
y = x
|
|
654
677
|
cmp = key <=> x.key
|
|
655
678
|
if cmp == 0
|
|
656
|
-
return
|
|
657
|
-
x.value = value
|
|
658
|
-
return true
|
|
679
|
+
return yield(x, false)
|
|
659
680
|
elsif cmp < 0
|
|
660
681
|
x = x.left
|
|
661
682
|
else
|
|
662
683
|
x = x.right
|
|
663
684
|
end
|
|
664
685
|
end
|
|
665
|
-
|
|
686
|
+
|
|
687
|
+
initial_value = yield(nil, true)
|
|
688
|
+
|
|
689
|
+
z = allocate_node(key, initial_value, Node::RED, @nil_node, @nil_node, @nil_node)
|
|
666
690
|
z.parent = y
|
|
667
691
|
if y == @nil_node
|
|
668
692
|
@root = z
|
|
@@ -679,14 +703,34 @@ class RBTree
|
|
|
679
703
|
if @min_node == @nil_node || (key <=> @min_node.key) < 0
|
|
680
704
|
@min_node = z
|
|
681
705
|
end
|
|
706
|
+
if @max_node == @nil_node || (key <=> @max_node.key) > 0
|
|
707
|
+
@max_node = z
|
|
708
|
+
end
|
|
682
709
|
|
|
683
|
-
@hash_index[key] = z
|
|
710
|
+
@hash_index[key] = z
|
|
684
711
|
true
|
|
685
712
|
end
|
|
686
713
|
|
|
714
|
+
# Traverses the tree in a specified direction (ascending or descending).
|
|
715
|
+
#
|
|
716
|
+
# @param direction [Boolean] true for descending, false for ascending
|
|
717
|
+
# @param min [Object] the lower bound (inclusive)
|
|
718
|
+
# @param max [Object] the upper bound (inclusive)
|
|
719
|
+
# @param include_min [Boolean] whether to include the lower bound
|
|
720
|
+
# @param include_max [Boolean] whether to include the upper bound
|
|
721
|
+
# @param safe [Boolean] whether to use safe traversal
|
|
722
|
+
# @yield [key, value] each key-value pair in the specified direction
|
|
723
|
+
# @return [void]
|
|
724
|
+
def traverse_range(direction, min, max, include_min, include_max, safe: false, &block)
|
|
725
|
+
if direction
|
|
726
|
+
traverse_range_desc(min, max, include_min, include_max, safe: safe, &block)
|
|
727
|
+
else
|
|
728
|
+
traverse_range_asc(min, max, include_min, include_max, safe: safe, &block)
|
|
729
|
+
end
|
|
730
|
+
end
|
|
731
|
+
|
|
687
732
|
# Traverses the tree in ascending order (in-order traversal).
|
|
688
733
|
#
|
|
689
|
-
# @param node [Node] the current node
|
|
690
734
|
# @param min [Object] the lower bound (inclusive)
|
|
691
735
|
# @param max [Object] the upper bound (inclusive)
|
|
692
736
|
# @param include_min [Boolean] whether to include the lower bound
|
|
@@ -694,56 +738,31 @@ class RBTree
|
|
|
694
738
|
# @param safe [Boolean] whether to use safe traversal
|
|
695
739
|
# @yield [key, value] each key-value pair in ascending order
|
|
696
740
|
# @return [void]
|
|
697
|
-
def traverse_range_asc(
|
|
741
|
+
def traverse_range_asc(min, max, include_min, include_max, safe: false, &block)
|
|
742
|
+
# O(1) Range Rejection
|
|
743
|
+
return if min && @max_node != @nil_node && (min <=> @max_node.key) > 0
|
|
744
|
+
return if min && @max_node != @nil_node && !include_min && (min <=> @max_node.key) == 0
|
|
745
|
+
|
|
746
|
+
# O(1) Bound Optimization
|
|
747
|
+
max = nil if max && @max_node != @nil_node && (cmp = max <=> @max_node.key) >= 0 && (cmp > 0 || include_max)
|
|
748
|
+
|
|
698
749
|
if safe
|
|
699
750
|
pair = !min ? find_min :
|
|
700
751
|
include_min && @hash_index[min]&.pair || find_successor(min)
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
pair = find_successor(current_key)
|
|
706
|
-
end
|
|
707
|
-
else
|
|
708
|
-
while pair && pair[0] < max
|
|
709
|
-
current_key = pair[0]
|
|
710
|
-
yield pair
|
|
711
|
-
pair = find_successor(current_key)
|
|
712
|
-
end
|
|
752
|
+
while pair && (!max || pair[0] < max)
|
|
753
|
+
current_key = pair[0]
|
|
754
|
+
yield pair
|
|
755
|
+
pair = find_successor(current_key)
|
|
713
756
|
end
|
|
714
757
|
yield pair if pair && max && include_max && pair[0] == max
|
|
715
758
|
else
|
|
716
|
-
stack =
|
|
717
|
-
|
|
718
|
-
while current != @nil_node || !stack.empty?
|
|
719
|
-
while current != @nil_node
|
|
720
|
-
if min && ((current.key <=> min) < 0 ||
|
|
721
|
-
(!include_min && (current.key <=> min) == 0))
|
|
722
|
-
current = current.right
|
|
723
|
-
else
|
|
724
|
-
stack << current
|
|
725
|
-
current = current.left
|
|
726
|
-
end
|
|
727
|
-
end
|
|
728
|
-
|
|
729
|
-
if !stack.empty?
|
|
730
|
-
current = stack.pop
|
|
731
|
-
|
|
732
|
-
if max && ((current.key <=> max) > 0 ||
|
|
733
|
-
(!include_max && (current.key <=> max) == 0))
|
|
734
|
-
return
|
|
735
|
-
else
|
|
736
|
-
yield current.pair
|
|
737
|
-
current = current.right
|
|
738
|
-
end
|
|
739
|
-
end
|
|
740
|
-
end
|
|
759
|
+
start_node, stack = resolve_startup_asc(min, include_min)
|
|
760
|
+
traverse_from_asc(start_node, stack, max, include_max, &block)
|
|
741
761
|
end
|
|
742
762
|
end
|
|
743
763
|
|
|
744
764
|
# Traverses the tree in descending order (reverse in-order traversal).
|
|
745
765
|
#
|
|
746
|
-
# @param node [Node] the current node
|
|
747
766
|
# @param min [Object] the lower bound (inclusive)
|
|
748
767
|
# @param max [Object] the upper bound (inclusive)
|
|
749
768
|
# @param include_min [Boolean] whether to include the lower bound
|
|
@@ -751,164 +770,218 @@ class RBTree
|
|
|
751
770
|
# @param safe [Boolean] whether to use safe traversal
|
|
752
771
|
# @yield [key, value] each key-value pair in descending order
|
|
753
772
|
# @return [void]
|
|
754
|
-
def traverse_range_desc(
|
|
773
|
+
def traverse_range_desc(min, max, include_min, include_max, safe: false, &block)
|
|
774
|
+
# O(1) Range Rejection
|
|
775
|
+
return if max && @min_node != @nil_node && (max <=> @min_node.key) < 0
|
|
776
|
+
return if max && @min_node != @nil_node && !include_max && (max <=> @min_node.key) == 0
|
|
777
|
+
|
|
778
|
+
# O(1) Bound Optimization
|
|
779
|
+
min = nil if min && @min_node != @nil_node && (cmp = min <=> @min_node.key) <= 0 && (cmp < 0 || include_min)
|
|
780
|
+
|
|
755
781
|
if safe
|
|
756
782
|
pair = !max ? find_max :
|
|
757
783
|
include_max && @hash_index[max]&.pair || find_predecessor(max)
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
pair = find_predecessor(current_key)
|
|
763
|
-
end
|
|
764
|
-
else
|
|
765
|
-
while pair && pair[0] > min
|
|
766
|
-
current_key = pair[0]
|
|
767
|
-
yield pair
|
|
768
|
-
pair = find_predecessor(current_key)
|
|
769
|
-
end
|
|
784
|
+
while pair && (!min || pair[0] > min)
|
|
785
|
+
current_key = pair[0]
|
|
786
|
+
yield pair
|
|
787
|
+
pair = find_predecessor(current_key)
|
|
770
788
|
end
|
|
771
789
|
yield pair if pair && min && include_min && pair[0] == min
|
|
772
790
|
else
|
|
773
|
-
stack =
|
|
774
|
-
|
|
775
|
-
while current != @nil_node || !stack.empty?
|
|
776
|
-
while current != @nil_node
|
|
777
|
-
if max && ((current.key <=> max) > 0 ||
|
|
778
|
-
(!include_max && (current.key <=> max) == 0))
|
|
779
|
-
current = current.left
|
|
780
|
-
else
|
|
781
|
-
stack << current
|
|
782
|
-
current = current.right
|
|
783
|
-
end
|
|
784
|
-
end
|
|
785
|
-
|
|
786
|
-
if !stack.empty?
|
|
787
|
-
current = stack.pop
|
|
788
|
-
|
|
789
|
-
if min && ((current.key <=> min) < 0 ||
|
|
790
|
-
(!include_min && (current.key <=> min) == 0))
|
|
791
|
-
return
|
|
792
|
-
else
|
|
793
|
-
yield current.pair
|
|
794
|
-
current = current.left
|
|
795
|
-
end
|
|
796
|
-
end
|
|
797
|
-
end
|
|
791
|
+
start_node, stack = resolve_startup_desc(max, include_max)
|
|
792
|
+
traverse_from_desc(start_node, stack, min, include_min, &block)
|
|
798
793
|
end
|
|
799
794
|
end
|
|
800
795
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
#
|
|
804
|
-
# @yield [key, value] each key-value pair in ascending order
|
|
805
|
-
# @return [void]
|
|
806
|
-
def traverse_all_asc(node, safe: false, &block) =
|
|
807
|
-
traverse_range_asc(node, nil, nil, false, false, safe: safe, &block)
|
|
808
|
-
|
|
809
|
-
# Traverses the tree in descending order (reverse in-order traversal).
|
|
810
|
-
#
|
|
811
|
-
# @param node [Node] the current node
|
|
812
|
-
# @yield [key, value] each key-value pair in descending order
|
|
813
|
-
# @return [void]
|
|
814
|
-
def traverse_all_desc(node, safe: false, &block) =
|
|
815
|
-
traverse_range_desc(node, nil, nil, false, false, safe: safe, &block)
|
|
816
|
-
|
|
817
|
-
# Traverses nodes with keys less than the specified key.
|
|
796
|
+
private
|
|
797
|
+
|
|
798
|
+
# Returns the predecessor of the given node.
|
|
818
799
|
#
|
|
819
|
-
# @param node [Node] the
|
|
820
|
-
# @
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
800
|
+
# @param node [Node] the node to find the predecessor of
|
|
801
|
+
# @return [Node] the predecessor node
|
|
802
|
+
def predecessor_node_of(node)
|
|
803
|
+
if node.left != @nil_node
|
|
804
|
+
return rightmost(node.left)
|
|
805
|
+
end
|
|
806
|
+
y = node.parent
|
|
807
|
+
while y != @nil_node && node == y.left
|
|
808
|
+
node = y
|
|
809
|
+
y = y.parent
|
|
810
|
+
end
|
|
811
|
+
y
|
|
812
|
+
end
|
|
825
813
|
|
|
826
|
-
#
|
|
814
|
+
# Returns the successor of the given node.
|
|
827
815
|
#
|
|
828
|
-
# @param node [Node] the
|
|
829
|
-
# @
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
816
|
+
# @param node [Node] the node to find the successor of
|
|
817
|
+
# @return [Node] the successor node
|
|
818
|
+
def successor_node_of(node)
|
|
819
|
+
if node.right != @nil_node
|
|
820
|
+
return leftmost(node.right)
|
|
821
|
+
end
|
|
822
|
+
y = node.parent
|
|
823
|
+
while y != @nil_node && node == y.right
|
|
824
|
+
node = y
|
|
825
|
+
y = y.parent
|
|
826
|
+
end
|
|
827
|
+
y
|
|
828
|
+
end
|
|
834
829
|
|
|
835
|
-
#
|
|
830
|
+
# Resolves the startup node for ascending traversal.
|
|
836
831
|
#
|
|
837
|
-
# @param
|
|
838
|
-
# @param
|
|
839
|
-
# @
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
832
|
+
# @param min [Object] the minimum key to traverse from
|
|
833
|
+
# @param include_min [Boolean] whether to include the minimum key
|
|
834
|
+
# @return [Array(Node, Array)] the starting node and stack
|
|
835
|
+
def resolve_startup_asc(min, include_min)
|
|
836
|
+
# 1. Use cached min if no lower bound or if min is less than tree min
|
|
837
|
+
if @min_node != @nil_node && (!min || (min <=> @min_node.key) < 0)
|
|
838
|
+
return [@min_node, reconstruct_stack_asc(@min_node)]
|
|
839
|
+
end
|
|
840
|
+
|
|
841
|
+
# 2. Use Hash index if key exists
|
|
842
|
+
if min && (node = @hash_index[min])
|
|
843
|
+
start_node = include_min ? node : successor_node_of(node)
|
|
844
|
+
return [start_node, reconstruct_stack_asc(start_node)]
|
|
845
|
+
end
|
|
843
846
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
847
|
+
# 3. Fallback to tree search from root
|
|
848
|
+
stack = []
|
|
849
|
+
current = @root
|
|
850
|
+
while current != @nil_node
|
|
851
|
+
if min && ((current.key <=> min) < 0 || (!include_min && (current.key <=> min) == 0))
|
|
852
|
+
current = current.right
|
|
853
|
+
else
|
|
854
|
+
stack << current
|
|
855
|
+
current = current.left
|
|
856
|
+
end
|
|
857
|
+
end
|
|
858
|
+
[@nil_node, stack]
|
|
859
|
+
end
|
|
852
860
|
|
|
853
|
-
#
|
|
854
|
-
#
|
|
855
|
-
# @param node [Node] the
|
|
856
|
-
# @
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
861
|
+
# Reconstructs the stack for ascending traversal.
|
|
862
|
+
#
|
|
863
|
+
# @param node [Node] the node to reconstruct the stack from
|
|
864
|
+
# @return [Array] the reconstructed stack
|
|
865
|
+
def reconstruct_stack_asc(node)
|
|
866
|
+
return [] if node == @nil_node
|
|
867
|
+
stack = []
|
|
868
|
+
curr = node
|
|
869
|
+
while (p = curr.parent) != @nil_node
|
|
870
|
+
stack << p if curr == p.left
|
|
871
|
+
curr = p
|
|
872
|
+
end
|
|
873
|
+
stack.reverse!
|
|
874
|
+
stack
|
|
875
|
+
end
|
|
864
876
|
|
|
865
|
-
# Traverses
|
|
877
|
+
# Traverses the tree in ascending order.
|
|
866
878
|
#
|
|
867
|
-
# @param
|
|
868
|
-
# @param
|
|
869
|
-
# @
|
|
870
|
-
# @
|
|
871
|
-
|
|
872
|
-
|
|
879
|
+
# @param current [Node] the current node
|
|
880
|
+
# @param stack [Array] the stack of nodes to traverse
|
|
881
|
+
# @param max [Object] the maximum key to traverse to
|
|
882
|
+
# @param include_max [Boolean] whether to include the maximum key
|
|
883
|
+
# @yield [Array(Object, Object)] each key-value pair
|
|
884
|
+
# @yieldparam key [Object] the key
|
|
885
|
+
# @yieldparam val [Object] the value
|
|
886
|
+
def traverse_from_asc(current, stack, max, include_max, &block)
|
|
887
|
+
while current != @nil_node || !stack.empty?
|
|
888
|
+
if current != @nil_node
|
|
889
|
+
if max
|
|
890
|
+
cmp = current.key <=> max
|
|
891
|
+
if cmp >= 0
|
|
892
|
+
yield current.pair if include_max && cmp == 0
|
|
893
|
+
return
|
|
894
|
+
end
|
|
895
|
+
end
|
|
896
|
+
yield current.pair
|
|
897
|
+
current = current.right
|
|
898
|
+
while current != @nil_node
|
|
899
|
+
stack << current
|
|
900
|
+
current = current.left
|
|
901
|
+
end
|
|
902
|
+
else
|
|
903
|
+
current = stack.pop
|
|
904
|
+
end
|
|
905
|
+
end
|
|
906
|
+
end
|
|
873
907
|
|
|
874
|
-
#
|
|
908
|
+
# Resolves the startup node for descending traversal.
|
|
875
909
|
#
|
|
876
|
-
# @param
|
|
877
|
-
# @param
|
|
878
|
-
# @
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
910
|
+
# @param max [Object] the maximum key to traverse from
|
|
911
|
+
# @param include_max [Boolean] whether to include the maximum key
|
|
912
|
+
# @return [Array(Node, Array)] the starting node and stack
|
|
913
|
+
def resolve_startup_desc(max, include_max)
|
|
914
|
+
# 1. Use cached max if no upper bound or if max is greater than tree max
|
|
915
|
+
if @max_node != @nil_node && (!max || (max <=> @max_node.key) > 0)
|
|
916
|
+
return [@max_node, reconstruct_stack_desc(@max_node)]
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
# 2. Use Hash index if key exists
|
|
920
|
+
if max && (node = @hash_index[max])
|
|
921
|
+
start_node = include_max ? node : predecessor_node_of(node)
|
|
922
|
+
return [start_node, reconstruct_stack_desc(start_node)]
|
|
923
|
+
end
|
|
882
924
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
925
|
+
# 3. Fallback to tree search from root
|
|
926
|
+
stack = []
|
|
927
|
+
current = @root
|
|
928
|
+
while current != @nil_node
|
|
929
|
+
if max && ((current.key <=> max) > 0 || (!include_max && (current.key <=> max) == 0))
|
|
930
|
+
current = current.left
|
|
931
|
+
else
|
|
932
|
+
stack << current
|
|
933
|
+
current = current.right
|
|
934
|
+
end
|
|
935
|
+
end
|
|
936
|
+
[@nil_node, stack]
|
|
937
|
+
end
|
|
891
938
|
|
|
892
|
-
#
|
|
893
|
-
#
|
|
894
|
-
# @param node [Node] the
|
|
895
|
-
# @
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
939
|
+
# Reconstructs the stack for descending traversal.
|
|
940
|
+
#
|
|
941
|
+
# @param node [Node] the node to reconstruct the stack from
|
|
942
|
+
# @return [Array] the reconstructed stack
|
|
943
|
+
def reconstruct_stack_desc(node)
|
|
944
|
+
return [] if node == @nil_node
|
|
945
|
+
stack = []
|
|
946
|
+
curr = node
|
|
947
|
+
while (p = curr.parent) != @nil_node
|
|
948
|
+
stack << p if curr == p.right
|
|
949
|
+
curr = p
|
|
950
|
+
end
|
|
951
|
+
stack.reverse!
|
|
952
|
+
stack
|
|
953
|
+
end
|
|
900
954
|
|
|
901
|
-
# Traverses
|
|
955
|
+
# Traverses the tree in descending order.
|
|
902
956
|
#
|
|
903
|
-
# @param
|
|
904
|
-
# @param
|
|
905
|
-
# @param
|
|
906
|
-
# @param include_min [Boolean] whether to include the
|
|
907
|
-
# @
|
|
908
|
-
# @
|
|
909
|
-
# @
|
|
910
|
-
def
|
|
911
|
-
|
|
957
|
+
# @param current [Node] the current node
|
|
958
|
+
# @param stack [Array] the stack of nodes to traverse
|
|
959
|
+
# @param min [Object] the minimum key to traverse to
|
|
960
|
+
# @param include_min [Boolean] whether to include the minimum key
|
|
961
|
+
# @yield [Array(Object, Object)] each key-value pair
|
|
962
|
+
# @yieldparam key [Object] the key
|
|
963
|
+
# @yieldparam val [Object] the value
|
|
964
|
+
def traverse_from_desc(current, stack, min, include_min, &block)
|
|
965
|
+
while current != @nil_node || !stack.empty?
|
|
966
|
+
if current != @nil_node
|
|
967
|
+
if min
|
|
968
|
+
cmp = current.key <=> min
|
|
969
|
+
if cmp <= 0
|
|
970
|
+
yield current.pair if include_min && cmp == 0
|
|
971
|
+
return
|
|
972
|
+
end
|
|
973
|
+
end
|
|
974
|
+
yield current.pair
|
|
975
|
+
current = current.left
|
|
976
|
+
while current != @nil_node
|
|
977
|
+
stack << current
|
|
978
|
+
current = current.right
|
|
979
|
+
end
|
|
980
|
+
else
|
|
981
|
+
current = stack.pop
|
|
982
|
+
end
|
|
983
|
+
end
|
|
984
|
+
end
|
|
912
985
|
|
|
913
986
|
# Restores red-black tree properties after insertion.
|
|
914
987
|
#
|
|
@@ -1012,6 +1085,10 @@ class RBTree
|
|
|
1012
1085
|
if next_min_node
|
|
1013
1086
|
@min_node = next_min_node
|
|
1014
1087
|
end
|
|
1088
|
+
if z == @max_node
|
|
1089
|
+
next_max_node = predecessor_node_of(z)
|
|
1090
|
+
@max_node = next_max_node
|
|
1091
|
+
end
|
|
1015
1092
|
|
|
1016
1093
|
value = z.value
|
|
1017
1094
|
release_node(z)
|
|
@@ -1104,6 +1181,13 @@ class RBTree
|
|
|
1104
1181
|
# @param key [Numeric] the target key
|
|
1105
1182
|
# @return [Node] the nearest node, or @nil_node if tree is empty
|
|
1106
1183
|
def find_nearest_node(key)
|
|
1184
|
+
raise ArgumentError, "key must be Numeric" unless key.is_a?(Numeric)
|
|
1185
|
+
|
|
1186
|
+
# If key is larger than max_key, return max_node
|
|
1187
|
+
return @max_node if @max_node != @nil_node && key >= @max_node.key
|
|
1188
|
+
# If key is smaller than min_key, return min_node
|
|
1189
|
+
return @min_node if @min_node != @nil_node && key <= @min_node.key
|
|
1190
|
+
|
|
1107
1191
|
current = @root
|
|
1108
1192
|
closest = @nil_node
|
|
1109
1193
|
min_dist = nil
|
|
@@ -1144,21 +1228,11 @@ class RBTree
|
|
|
1144
1228
|
# @param key [Object] the reference key
|
|
1145
1229
|
# @return [Node] the predecessor node, or @nil_node if none exists
|
|
1146
1230
|
def find_predecessor_node(key)
|
|
1147
|
-
#
|
|
1231
|
+
# If key is larger than max_key, return max_node
|
|
1232
|
+
return @max_node if max_key && (key <=> max_key) > 0
|
|
1233
|
+
# If key exists using O(1) hash lookup, return predecessor node
|
|
1148
1234
|
if (node = @hash_index[key])
|
|
1149
|
-
|
|
1150
|
-
if node.left != @nil_node
|
|
1151
|
-
return rightmost(node.left)
|
|
1152
|
-
else
|
|
1153
|
-
# Walk up to find first ancestor where we came from the right
|
|
1154
|
-
current = node
|
|
1155
|
-
parent = current.parent
|
|
1156
|
-
while parent != @nil_node && current == parent.left
|
|
1157
|
-
current = parent
|
|
1158
|
-
parent = parent.parent
|
|
1159
|
-
end
|
|
1160
|
-
return parent
|
|
1161
|
-
end
|
|
1235
|
+
return predecessor_node_of(node)
|
|
1162
1236
|
end
|
|
1163
1237
|
|
|
1164
1238
|
# Key doesn't exist: descend tree tracking the best candidate
|
|
@@ -1194,25 +1268,14 @@ class RBTree
|
|
|
1194
1268
|
# @param key [Object] the reference key
|
|
1195
1269
|
# @return [Node] the successor node, or @nil_node if none exists
|
|
1196
1270
|
def find_successor_node(key)
|
|
1271
|
+
# If key is larger than or equal to max_key, return nil
|
|
1272
|
+
return @nil_node if max_key && (key <=> max_key) >= 0
|
|
1197
1273
|
# If key is smaller than min_key, return min_node
|
|
1198
|
-
return @min_node if min_key && key <
|
|
1199
|
-
#
|
|
1274
|
+
return @min_node if min_key && (key <=> min_key) < 0
|
|
1275
|
+
# If key exists using O(1) hash lookup, return successor node
|
|
1200
1276
|
if (node = @hash_index[key])
|
|
1201
|
-
|
|
1202
|
-
if node.right != @nil_node
|
|
1203
|
-
return leftmost(node.right)
|
|
1204
|
-
else
|
|
1205
|
-
# Walk up to find first ancestor where we came from the left
|
|
1206
|
-
current = node
|
|
1207
|
-
parent = current.parent
|
|
1208
|
-
while parent != @nil_node && current == parent.right
|
|
1209
|
-
current = parent
|
|
1210
|
-
parent = parent.parent
|
|
1211
|
-
end
|
|
1212
|
-
return parent
|
|
1213
|
-
end
|
|
1277
|
+
return successor_node_of(node)
|
|
1214
1278
|
end
|
|
1215
|
-
|
|
1216
1279
|
# Key doesn't exist: descend tree tracking the best candidate
|
|
1217
1280
|
current = @root
|
|
1218
1281
|
successor = @nil_node
|
|
@@ -1268,7 +1331,7 @@ class RBTree
|
|
|
1268
1331
|
# Returns the maximum key-value pair.
|
|
1269
1332
|
#
|
|
1270
1333
|
# @return [Array(Object, Object), nil] a two-element array [key, value], or nil if tree is empty
|
|
1271
|
-
def find_max = ((n =
|
|
1334
|
+
def find_max = ((n = @max_node) != @nil_node) && n.pair
|
|
1272
1335
|
|
|
1273
1336
|
# Performs a left rotation on the given node.
|
|
1274
1337
|
#
|
|
@@ -1499,7 +1562,6 @@ class MultiRBTree < RBTree
|
|
|
1499
1562
|
# tree.get(1, last: true) # => "second"
|
|
1500
1563
|
def value(key, last: false) = @hash_index[key]&.value&.send(last ? :last : :first)
|
|
1501
1564
|
alias :get :value
|
|
1502
|
-
alias :[] :value
|
|
1503
1565
|
|
|
1504
1566
|
# Retrieves the first value associated with the given key.
|
|
1505
1567
|
#
|
|
@@ -1673,47 +1735,15 @@ class MultiRBTree < RBTree
|
|
|
1673
1735
|
# tree.insert(1, 'first')
|
|
1674
1736
|
# tree.insert(1, 'second') # adds another value for key 1
|
|
1675
1737
|
def insert_entry(key, value, **)
|
|
1676
|
-
|
|
1677
|
-
node.value << value
|
|
1738
|
+
insert_entry_generic(key) do |node, is_new|
|
|
1678
1739
|
@value_count += 1
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
y = @nil_node
|
|
1682
|
-
x = @root
|
|
1683
|
-
while x != @nil_node
|
|
1684
|
-
y = x
|
|
1685
|
-
cmp = key <=> x.key
|
|
1686
|
-
if cmp == 0
|
|
1687
|
-
x.value << value
|
|
1688
|
-
@value_count += 1
|
|
1689
|
-
return true
|
|
1690
|
-
elsif cmp < 0
|
|
1691
|
-
x = x.left
|
|
1740
|
+
if is_new
|
|
1741
|
+
[value]
|
|
1692
1742
|
else
|
|
1693
|
-
|
|
1743
|
+
node.value << value
|
|
1744
|
+
true
|
|
1694
1745
|
end
|
|
1695
1746
|
end
|
|
1696
|
-
z = allocate_node(key, [value], Node::RED, @nil_node, @nil_node, @nil_node)
|
|
1697
|
-
z.parent = y
|
|
1698
|
-
if y == @nil_node
|
|
1699
|
-
@root = z
|
|
1700
|
-
elsif (key <=> y.key) < 0
|
|
1701
|
-
y.left = z
|
|
1702
|
-
else
|
|
1703
|
-
y.right = z
|
|
1704
|
-
end
|
|
1705
|
-
z.left = @nil_node
|
|
1706
|
-
z.right = @nil_node
|
|
1707
|
-
z.color = Node::RED
|
|
1708
|
-
insert_fixup(z)
|
|
1709
|
-
@value_count += 1
|
|
1710
|
-
|
|
1711
|
-
if @min_node == @nil_node || (key <=> @min_node.key) < 0
|
|
1712
|
-
@min_node = z
|
|
1713
|
-
end
|
|
1714
|
-
|
|
1715
|
-
@hash_index[key] = z # Add to hash index
|
|
1716
|
-
true
|
|
1717
1747
|
end
|
|
1718
1748
|
|
|
1719
1749
|
# Traverses the tree in ascending order, yielding each key-value pair.
|