tree_map 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,671 @@
1
+ require 'pp'
2
+ require 'set'
3
+
4
+ # TreeMap is a Ruby port of https://android.googlesource.com/platform/libcore.git/+/android-6.0.1_r32/luni/src/main/java/java/util/TreeMap.java
5
+ # This is an AVL tree based implementation of Java's java.util.TreeMap structure.
6
+ # It implements Java's java.util.NavigableMap interface.
7
+ # Warning: Not all of the reference implementation has been ported.
8
+ class TreeMap
9
+ include Enumerable
10
+
11
+ module Relation
12
+ LOWER = 1
13
+ FLOOR = 2
14
+ EQUAL = 3
15
+ CREATE = 4
16
+ CEILING = 5
17
+ HIGHER = 6
18
+
19
+ def self.for_order(relation, ascending)
20
+ if ascending
21
+ relation
22
+ else
23
+ case relation
24
+ when LOWER
25
+ HIGHER
26
+ when FLOOR
27
+ CEILING
28
+ when EQUAL
29
+ EQUAL
30
+ when CEILING
31
+ FLOOR
32
+ when HIGHER
33
+ LOWER
34
+ else
35
+ raise "Unknown relation: #{relation.inspect}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ class Node
42
+ attr_accessor :parent, :left, :right, :key, :value, :height
43
+
44
+ def initialize(parent, key)
45
+ @parent = parent
46
+ @left = nil
47
+ @right = nil
48
+ @key = key
49
+ @value = nil
50
+ @height = 1
51
+ end
52
+
53
+ def copy(parent)
54
+ result = Node.new(@parent, @key)
55
+ if @left
56
+ result.left = @left.copy(result)
57
+ end
58
+ if @right
59
+ result.right = @right.copy(result)
60
+ end
61
+ result.value = @value
62
+ result.height = @height
63
+ result
64
+ end
65
+
66
+ def set_value(new_value)
67
+ old_value = @value
68
+ @value = new_value
69
+ old_value
70
+ end
71
+
72
+ def ==(other)
73
+ if other.is_a?(Node)
74
+ @key == other.key && @value == other.value
75
+ else
76
+ false
77
+ end
78
+ end
79
+
80
+ alias eql? ==
81
+
82
+ def hash
83
+ (key.nil? ? 0 : key.hash) ^ (value.nil? ? 0 : value.hash)
84
+ end
85
+
86
+ def to_s
87
+ "#{@key}=#{@value}"
88
+ end
89
+
90
+ # Returns the next node in an inorder traversal, or null if this is the last node in the tree.
91
+ def next_node
92
+ return @right.first if @right
93
+
94
+ node = self
95
+ parent = node.parent
96
+ while parent
97
+ if parent.left == node
98
+ return parent
99
+ end
100
+ node = parent
101
+ parent = node.parent
102
+ end
103
+ nil
104
+ end
105
+
106
+ # Returns the previous node in an inorder traversal, or null if this is the first node in the tree.
107
+ def prev_node
108
+ return @left.last if @left
109
+
110
+ node = self
111
+ parent = node.parent
112
+ while parent
113
+ if parent.right == node
114
+ return parent
115
+ end
116
+ node = parent
117
+ parent = node.parent
118
+ end
119
+ nil
120
+ end
121
+
122
+ # Returns the first node in this subtree.
123
+ def first
124
+ node = self
125
+ child = node.left
126
+ while child
127
+ node = child
128
+ child = node.left
129
+ end
130
+ node
131
+ end
132
+
133
+ # Returns the last node in this subtree.
134
+ def last
135
+ node = self
136
+ child = node.right
137
+ while child
138
+ node = child
139
+ child = node.right
140
+ end
141
+ node
142
+ end
143
+ end
144
+
145
+ NaturalOrder = ->(this, that) { this <=> that }
146
+
147
+ attr_accessor :comparator, :root, :size
148
+
149
+ # comparator is a function of the form: (this, that) -> int ; where int is -1 if this < that, 0 if this == that, and 1 if this > that
150
+ def initialize(comparator = NaturalOrder)
151
+ @comparator = comparator
152
+ @root = nil
153
+ @size = 0
154
+ @mod_count = 0
155
+ end
156
+
157
+ def empty?
158
+ @size == 0
159
+ end
160
+
161
+ def get(key)
162
+ entry = find_by_object(key)
163
+ entry.value if entry
164
+ end
165
+
166
+ alias [] get
167
+
168
+ def contains_key?(key)
169
+ find_by_object(key)
170
+ end
171
+
172
+ def put(key, value)
173
+ put_internal(key, value)
174
+ end
175
+
176
+ def clear
177
+ @root = nil
178
+ @size = 0
179
+ @mod_count += 1
180
+ end
181
+
182
+ def remove(key)
183
+ node = remove_internal_by_key(key)
184
+ node.value if node
185
+ end
186
+
187
+ def put_internal(key, value)
188
+ created = find(key, Relation::CREATE)
189
+ result = created.value
190
+ created.value = value
191
+ result
192
+ end
193
+
194
+ # Returns the node at or adjacent to the given key, creating it if requested.
195
+ def find(key, relation)
196
+ if @root.nil?
197
+ if relation == Relation::CREATE
198
+ @root = Node.new(nil, key)
199
+ @size = 1
200
+ @mod_count += 1
201
+ return @root
202
+ else
203
+ return nil
204
+ end
205
+ end
206
+
207
+ nearest = @root
208
+ while true
209
+ comparison = @comparator.call(key, nearest.key)
210
+
211
+ # we found the requested key
212
+ if comparison == 0
213
+ case relation
214
+ when Relation::LOWER
215
+ return nearest.prev_node
216
+ when Relation::FLOOR, Relation::EQUAL, Relation::CREATE, Relation::CEILING
217
+ return nearest
218
+ when Relation::HIGHER
219
+ return nearest.next_node
220
+ end
221
+ end
222
+
223
+ child = (comparison < 0) ? nearest.left : nearest.right
224
+ if child
225
+ nearest = child
226
+ next # continue
227
+ end
228
+
229
+ # We found a nearest node. Every key not in the tree has up to two nearest nodes, one lower and one higher.
230
+ if comparison < 0 # nearest.key is higher
231
+ case relation
232
+ when Relation::LOWER, Relation::FLOOR
233
+ return nearest.prev_node
234
+ when Relation::CEILING, Relation::HIGHER
235
+ return nearest
236
+ when Relation::EQUAL
237
+ return nil
238
+ when Relation::CREATE
239
+ created = Node.new(nearest, key)
240
+ nearest.left = created
241
+ @size += 1
242
+ @mod_count += 1
243
+ rebalance(nearest, true)
244
+ return created
245
+ end
246
+ else # comparison > 0 ; nearest.key is lower
247
+ case relation
248
+ when Relation::LOWER, Relation::FLOOR
249
+ return nearest
250
+ when Relation::CEILING, Relation::HIGHER
251
+ return nearest.next_node
252
+ when Relation::EQUAL
253
+ return nil
254
+ when Relation::CREATE
255
+ created = Node.new(nearest, key)
256
+ nearest.right = created
257
+ @size += 1
258
+ @mod_count += 1
259
+ rebalance(nearest, true)
260
+ return created
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ # returns a Node
267
+ def find_by_object(key)
268
+ find(key, Relation::EQUAL)
269
+ end
270
+
271
+ # entry is a key-value pair in an array: [key, value]
272
+ # Returns this map's entry that has the same key and value as <entry>, or null if this map has no such entry.
273
+ #
274
+ # This method uses the comparator for key equality rather than <equals>. If this map's comparator isn't consistent with equals,
275
+ # then {@code remove()} and {@code contains()} will violate the collections API.
276
+ #
277
+ # returns a Node
278
+ def find_by_entry(key, value)
279
+ key, value = *entry
280
+ mine = find_by_object(key)
281
+ mine if mine && mine.value == value
282
+ end
283
+
284
+ # Removes {@code node} from this tree, rearranging the tree's structure as necessary.
285
+ # return value not meaningful
286
+ def remove_internal(node)
287
+ left = node.left
288
+ right = node.right
289
+ original_parent = node.parent
290
+
291
+ if left && right
292
+ # To remove a node with both left and right subtrees, move an adjacent node from one of those subtrees into this node's place.
293
+ # Removing the adjacent node may change this node's subtrees. This node may no longer have two subtrees once the adjacent node is gone!
294
+
295
+ adjacent = left.height > right.height ? left.last : right.first
296
+ remove_internal(adjacent) # takes care of rebalance and size--
297
+
298
+ left_height = 0
299
+ left = node.left
300
+ if left
301
+ left_height = left.height
302
+ adjacent.left = left
303
+ left.parent = adjacent
304
+ node.left = nil
305
+ end
306
+ right_height = 0
307
+ right = node.right
308
+ if right
309
+ right_height = right.height
310
+ adjacent.right = right
311
+ right.parent = adjacent
312
+ node.right = nil
313
+ end
314
+ adjacent.height = [left_height, right_height].max + 1
315
+ replace_in_parent(node, adjacent)
316
+ return
317
+ elsif left
318
+ replace_in_parent(node, left)
319
+ node.left = nil
320
+ elsif right
321
+ replace_in_parent(node, right)
322
+ node.right = nil
323
+ else
324
+ replace_in_parent(node, nil)
325
+ end
326
+
327
+ rebalance(original_parent, false)
328
+ @size -= 1
329
+ @mod_count -= 1
330
+ end
331
+
332
+ def remove_internal_by_key(key)
333
+ node = find_by_object(key)
334
+ if node
335
+ remove_internal(node)
336
+ end
337
+ node
338
+ end
339
+
340
+ def replace_in_parent(node, replacement)
341
+ parent = node.parent
342
+ node.parent = nil
343
+ if replacement
344
+ replacement.parent = parent
345
+ end
346
+
347
+ if parent
348
+ if parent.left == node
349
+ parent.left = replacement
350
+ else
351
+ # assert (parent.right == node)
352
+ parent.right = replacement
353
+ end
354
+ else
355
+ @root = replacement
356
+ end
357
+ end
358
+
359
+ # Rebalances the tree by making any AVL rotations necessary between the newly-unbalanced node and the tree's root.
360
+ #
361
+ # @param insert true if the node was unbalanced by an insert; false if it was by a removal.
362
+ def rebalance(unbalanced, insert)
363
+ node = unbalanced
364
+ while node
365
+ left = node.left
366
+ right = node.right
367
+ left_height = left ? left.height : 0
368
+ right_height = right ? right.height : 0
369
+
370
+ delta = left_height - right_height
371
+ if delta == -2
372
+ right_left = right.left
373
+ right_right = right.right
374
+ right_right_height = right_right ? right_right.height : 0
375
+ right_left_height = right_left ? right_left.height : 0
376
+
377
+ right_delta = right_left_height - right_right_height
378
+ if right_delta == -1 || (right_delta == 0 && !insert)
379
+ rotate_left(node)
380
+ else
381
+ # assert (right_delta == 1)
382
+ rotate_right(right) # AVL right left
383
+ rotate_left(node)
384
+ end
385
+ break if insert # no further rotations will be necessary
386
+ elsif delta == 2
387
+ left_left = left.left
388
+ left_right = left.right
389
+ left_right_height = left_right ? left_right.height : 0
390
+ left_left_height = left_left ? left_left.height : 0
391
+
392
+ left_delta = left_left_height - left_right_height
393
+ if left_delta == 1 || (left_delta == 0 && !insert)
394
+ rotate_right(node) # AVL left left
395
+ else
396
+ # assert (left_delta == -1)
397
+ rotate_left(left) # AVL left right
398
+ rotate_right(node)
399
+ end
400
+ break if insert
401
+ elsif delta == 0
402
+ node.height = left_height + 1 # left_height == right_height
403
+ break if insert
404
+ else
405
+ # assert (delta == -1 || delta == 1)
406
+ node.height = [left_height, right_height].max + 1
407
+ break unless insert # the height hasn't changed, so rebalancing is done!
408
+ end
409
+
410
+ node = node.parent
411
+ end
412
+ end
413
+
414
+ # Rotates the subtree so that its root's right child is the new root
415
+ def rotate_left(root)
416
+ left = root.left
417
+ pivot = root.right
418
+ pivot_left = pivot.left
419
+ pivot_right = pivot.right
420
+
421
+ # move the pivot's left child to the root's right
422
+ root.right = pivot_left
423
+ if pivot_left
424
+ pivot_left.parent = root
425
+ end
426
+
427
+ replace_in_parent(root, pivot)
428
+
429
+ # move the root to the pivot's left
430
+ pivot.left = root
431
+ root.parent = pivot
432
+
433
+ # fix heights
434
+ root.height = [left ? left.height : 0, pivot_left ? pivot_left.height : 0].max + 1
435
+ pivot.height = [root.height, pivot_right ? pivot_right.height : 0].max + 1
436
+ end
437
+
438
+ # Rotates the subtree so that its root's left child is the new root
439
+ def rotate_right(root)
440
+ pivot = root.left
441
+ right = root.right
442
+ pivot_left = pivot.left
443
+ pivot_right = pivot.right
444
+
445
+ # move the pivot's right child to the root's left
446
+ root.left = pivot_right
447
+ if pivot_right
448
+ pivot_right.parent = root
449
+ end
450
+
451
+ replace_in_parent(root, pivot)
452
+
453
+ # move the root to the pivot's right
454
+ pivot.right = root
455
+ root.parent = pivot
456
+
457
+ # fix heights
458
+ root.height = [right ? right.height : 0, pivot_right ? pivot_right.height : 0].max + 1
459
+ pivot.height = [root.height, pivot_left ? pivot_left.height : 0].max + 1
460
+ end
461
+
462
+ # Navigable Methods
463
+
464
+ # Returns a key-value mapping associated with the least key in this map, or null if the map is empty.
465
+ def first_entry
466
+ root.first if root
467
+ end
468
+
469
+ def internal_poll_first_entry
470
+ if root
471
+ result = root.first
472
+ remove_internal(result)
473
+ result
474
+ end
475
+ end
476
+
477
+ def poll_first_entry
478
+ internal_poll_first_entry
479
+ end
480
+
481
+ def first_key
482
+ raise "No such element." unless root
483
+ root.first.key
484
+ end
485
+
486
+ # Returns a key-value mapping associated with the greatest key in this map, or null if the map is empty.
487
+ def last_entry
488
+ root.last if root
489
+ end
490
+
491
+ def internal_poll_last_entry
492
+ if root
493
+ result = root.last
494
+ remove_internal(result)
495
+ result
496
+ end
497
+ end
498
+
499
+ def poll_last_entry
500
+ internal_poll_last_entry
501
+ end
502
+
503
+ def last_key
504
+ raise "No such element." unless root
505
+ root.last.key
506
+ end
507
+
508
+ # Returns a key-value mapping associated with the greatest key strictly less than the given key, or null if there is no such key.
509
+ def lower_entry(key)
510
+ find(key, Relation::LOWER)
511
+ end
512
+
513
+ # Returns the greatest key strictly less than the given key, or null if there is no such key.
514
+ def lower_key(key)
515
+ entry = find(key, Relation::LOWER)
516
+ entry.key if entry
517
+ end
518
+
519
+ # Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key.
520
+ def floor_entry(key)
521
+ find(key, Relation::FLOOR)
522
+ end
523
+
524
+ # Returns the greatest key less than or equal to the given key, or null if there is no such key.
525
+ def floor_key(key)
526
+ entry = find(key, Relation::FLOOR)
527
+ entry.key if entry
528
+ end
529
+
530
+ # Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
531
+ def ceiling_entry(key)
532
+ find(key, Relation::CEILING)
533
+ end
534
+
535
+ # Returns the least key greater than or equal to the given key, or null if there is no such key.
536
+ def ceiling_key(key)
537
+ entry = find(key, Relation::CEILING)
538
+ entry.key if entry
539
+ end
540
+
541
+ # Returns a key-value mapping associated with the least key strictly greater than the given key, or null if there is no such key.
542
+ def higher_entry(key)
543
+ find(key, Relation::HIGHER)
544
+ end
545
+
546
+ # Returns the least key strictly greater than the given key, or null if there is no such key.
547
+ def higher_key(key)
548
+ entry = find(key, Relation::HIGHER)
549
+ entry.key if entry
550
+ end
551
+
552
+ # View factory methods
553
+
554
+ def entry_set
555
+ Set.new(each_node.to_a)
556
+ end
557
+
558
+ def key_set
559
+ Set.new(each_node.map(&:key))
560
+ end
561
+
562
+ alias keys key_set
563
+
564
+ def values
565
+ each_node.map(&:value)
566
+ end
567
+
568
+ # This can be called in 1 of 2 ways:
569
+ # sub_map(from_inclusive, to_exclusive)
570
+ # OR
571
+ # sub_map(from, from_inclusive, to, to_inclusive)
572
+ def sub_map(*args)
573
+ case args.count
574
+ when 2
575
+ from_inclusive, to_exclusive = *args
576
+ BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, to_exclusive, Bound::EXCLUSIVE)
577
+ when 4
578
+ from, from_inclusive, to, to_inclusive = *args
579
+ from_bound = from_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
580
+ to_bound = to_inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
581
+ BoundedMap.new(self, true, from, from_bound, to, to_bound);
582
+ end
583
+ end
584
+
585
+ # This can be called in 1 of 2 ways:
586
+ # head_map(to_exclusive)
587
+ # OR
588
+ # head_map(to, inclusive)
589
+ def head_map(*args)
590
+ case args.count
591
+ when 1
592
+ to_exclusive = args.first
593
+ BoundedMap.new(self, true, nil, Bound::NO_BOUND, to_exclusive, Bound::EXCLUSIVE)
594
+ when 2
595
+ to, inclusive = *args
596
+ to_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
597
+ BoundedMap.new(self, true, nil, Bound::NO_BOUND, to, to_bound)
598
+ end
599
+ end
600
+
601
+ # This can be called in 1 of 2 ways:
602
+ # tail_map(from_inclusive)
603
+ # OR
604
+ # tail_map(from, inclusive)
605
+ def tail_map(*args)
606
+ case args.count
607
+ when 1
608
+ from_inclusive = args.first
609
+ BoundedMap.new(self, true, from_inclusive, Bound::INCLUSIVE, nil, Bound::NO_BOUND)
610
+ when 2
611
+ from, inclusive = *args
612
+ from_bound = inclusive ? Bound::INCLUSIVE : Bound::EXCLUSIVE
613
+ BoundedMap.new(self, true, from, from_bound, nil, Bound::NO_BOUND)
614
+ end
615
+ end
616
+
617
+ def descending_map
618
+ BoundedMap.new(self, false, nil, Bound::NO_BOUND, nil, Bound::NO_BOUND)
619
+ end
620
+
621
+ # Tree traversal methods
622
+
623
+ # in-order traversal of nodes in tree
624
+ class NodeIterator
625
+ def initialize(next_node)
626
+ @next_node = next_node
627
+ @last_node = nil
628
+ end
629
+
630
+ def has_next?
631
+ !!@next_node
632
+ end
633
+
634
+ def step_forward
635
+ if @next_node
636
+ @last_node = @next_node
637
+ @next_node = @next_node.next_node
638
+ @last_node
639
+ end
640
+ end
641
+
642
+ def step_backward
643
+ if @next_node
644
+ @last_node = @next_node
645
+ @next_node = @next_node.prev_node
646
+ @last_node
647
+ end
648
+ end
649
+ end
650
+
651
+ # each {|k,v| puts "#{k}->#{v}"}
652
+ def each(&blk)
653
+ if block_given?
654
+ each_node {|node| blk.call(node.key, node.value) }
655
+ else
656
+ enum_for(:each)
657
+ end
658
+ end
659
+
660
+ # each_node {|node| puts "#{node.key}->#{node.value}"}
661
+ def each_node
662
+ if block_given?
663
+ iter = NodeIterator.new(@root.first)
664
+ while iter.has_next?
665
+ yield iter.step_forward()
666
+ end
667
+ else
668
+ enum_for(:each_node)
669
+ end
670
+ end
671
+ end
@@ -0,0 +1,3 @@
1
+ module TreeMap
2
+ VERSION = "1.0.4"
3
+ end
data/lib/tree_map.rb ADDED
@@ -0,0 +1,3 @@
1
+ require "tree_map/version"
2
+ require 'treemap/tree_map'
3
+ require 'treemap/bounded_map'
data/tree_map.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "tree_map/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "tree_map"
7
+ spec.version = TreeMap::VERSION
8
+ spec.authors = ["tianl677"]
9
+ spec.email = ["tianlu1677@gmail.com"]
10
+
11
+ spec.summary = %q{avl tree }
12
+ spec.description = %q{avl tree .}
13
+ spec.homepage = "https://github.com/tianlu1677/tree_map"
14
+ spec.license = "MIT"
15
+
16
+ # spec.metadata["allowed_push_host"] = "https://github.com/tianlu1677/tree_map"
17
+
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = "https://github.com/tianlu1677/tree_map."
20
+ spec.metadata["changelog_uri"] = "https://github.com/tianlu1677/tree_map."
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
25
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_development_dependency "bundler", "~> 2.0"
32
+ spec.add_development_dependency "rake", "~> 10.0"
33
+ spec.add_development_dependency "minitest", "~> 5.0"
34
+ end