binary_search_tree 1.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,480 @@
1
+ class BinaryNode
2
+ attr_accessor :height, :parent, :left, :right, :key, :value
3
+
4
+ def initialize key, value, parent
5
+ @key = key
6
+ @value = value
7
+ @parent = parent
8
+ @height = 0
9
+ end
10
+
11
+ def is_leaf?
12
+ height.zero?
13
+ end
14
+
15
+ def max_children_height
16
+ if left.present? && right.present?
17
+ [left.height, right.height].max
18
+ elsif left.present?
19
+ left.height
20
+ elsif right.present?
21
+ right.height
22
+ else
23
+ -1
24
+ end
25
+ end
26
+
27
+ def balance_factor
28
+ (left.height rescue -1) - (right.height rescue -1)
29
+ end
30
+ end
31
+
32
+
33
+ class BinarySearchTree
34
+ attr_reader :size, :root
35
+
36
+ def initialize logger=nil
37
+ @logger = logger
38
+ clear
39
+ end
40
+
41
+ def clear
42
+ @root = nil
43
+ @size = 0
44
+ end
45
+
46
+ def empty?
47
+ @root.nil?
48
+ end
49
+
50
+ def find key
51
+ @num_comparisons = 0
52
+ node = locate key, @root
53
+ @logger.debug "find operation completed in #{@num_comparisons} lookups..." if @logger.present?
54
+ node
55
+ end
56
+
57
+ def find_value value
58
+ find_value_ex @root, value
59
+ end
60
+
61
+ def min
62
+ @min ||= locate_min @root
63
+ end
64
+
65
+ def max
66
+ @max ||= locate_max @root
67
+ end
68
+
69
+ def insert element, value
70
+ put element, value, @root, nil
71
+ end
72
+
73
+ def remove node_or_key
74
+ delete node_or_key
75
+ end
76
+
77
+ def remove_min
78
+ delete min
79
+ end
80
+
81
+ def nodes
82
+ @nodes = []
83
+ serialize_nodes @root
84
+ @nodes
85
+ end
86
+
87
+ def == other_bst
88
+ compare @root, other_bst.root
89
+ end
90
+
91
+ private
92
+ def invalidate_cached_values
93
+ @min = @max = nil
94
+ end
95
+
96
+ def locate target, leaf
97
+ @num_comparisons += 1
98
+ if leaf.nil?
99
+ return nil
100
+ elsif leaf.key < target
101
+ return locate target, leaf.right
102
+ elsif leaf.key > target
103
+ return locate target, leaf.left
104
+ elsif leaf.key == target
105
+ return leaf
106
+ end
107
+ end
108
+
109
+ def locate_min leaf
110
+ return nil if leaf.nil?
111
+ return leaf if leaf.left.nil?
112
+ return locate_min leaf.left
113
+ end
114
+
115
+ def locate_max leaf
116
+ return nil if leaf.nil?
117
+ return leaf if leaf.right.nil?
118
+ return locate_max leaf.right
119
+ end
120
+
121
+ def recompute_heights start_from_node
122
+ changed = true
123
+ node = start_from_node
124
+ while node && changed
125
+ old_height = node.height
126
+ if node.right.present? || node.left.present?
127
+ node.height = node.max_children_height + 1
128
+ else
129
+ node.height = 0
130
+ end
131
+ changed = node.height != old_height
132
+ node = node.parent
133
+ end
134
+ end
135
+
136
+ def put element, value, leaf, parent, link_type=nil
137
+ if leaf.nil?
138
+ leaf = BinaryNode.new element, value, parent
139
+ @size += 1
140
+ invalidate_cached_values
141
+ if parent.present?
142
+ if 'left' == link_type
143
+ parent.left = leaf
144
+ else
145
+ parent.right = leaf
146
+ end
147
+ else
148
+ @root = leaf
149
+ end
150
+ if parent.present? && parent.height.zero?
151
+ node = parent
152
+ node_to_rebalance = nil
153
+ while node.present?
154
+ node.height = node.max_children_height + 1
155
+ if node.balance_factor.abs > 1
156
+ node_to_rebalance = node
157
+ break
158
+ end
159
+ node = node.parent
160
+ end
161
+ rebalance node_to_rebalance if node_to_rebalance.present?
162
+ end
163
+
164
+ elsif leaf.key < element
165
+ put element, value, leaf.right, leaf, "right"
166
+ elsif leaf.key > element
167
+ put element, value, leaf.left, leaf, "left"
168
+ elsif leaf.key == element
169
+ leaf.value = value
170
+ end
171
+ end
172
+
173
+ def find_value_ex leaf, value
174
+ if leaf.present?
175
+ node_with_value = find_value_ex leaf.left, value
176
+ return node_with_value if node_with_value.present?
177
+ return leaf if leaf.value == value
178
+ node_with_value = find_value_ex leaf.right, value
179
+ return node_with_value if node_with_value.present?
180
+ end
181
+ nil
182
+ end
183
+
184
+ def serialize_nodes leaf
185
+ if !leaf.nil?
186
+ serialize_nodes leaf.left
187
+ @nodes += [leaf]
188
+ serialize_nodes leaf.right
189
+ end
190
+ end
191
+
192
+ def compare leaf, other_bst_leaf
193
+ if leaf.present? && other_bst_leaf.present?
194
+ leaf.value == other_bst_leaf.value &&
195
+ compare(leaf.left, other_bst_leaf.left) &&
196
+ compare(leaf.right, other_bst_leaf.right)
197
+ else
198
+ leaf.nil? && other_bst_leaf.nil?
199
+ end
200
+ end
201
+
202
+ def assert condition
203
+ raise "assertion failed" unless condition
204
+ end
205
+
206
+ def rebalance node_to_rebalance
207
+ a = node_to_rebalance
208
+ f = a.parent #allowed to be NULL
209
+ if node_to_rebalance.balance_factor == -2
210
+ if node_to_rebalance.right.balance_factor <= 0
211
+ # """Rebalance, case RRC """
212
+ b = a.right
213
+ c = b.right
214
+ assert a.present? && b.present? && c.present?
215
+ a.right = b.left
216
+ if a.right.present?
217
+ a.right.parent = a
218
+ end
219
+ b.left = a
220
+ a.parent = b
221
+ if f.nil?
222
+ @root = b
223
+ @root.parent = nil
224
+ else
225
+ if f.right == a
226
+ f.right = b
227
+ else
228
+ f.left = b
229
+ end
230
+ b.parent = f
231
+ end
232
+ recompute_heights a
233
+ recompute_heights b.parent
234
+ else
235
+ # """Rebalance, case RLC """
236
+ b = a.right
237
+ c = b.left
238
+ assert a.present? && b.present? && c.present?
239
+ b.left = c.right
240
+ if b.left.present?
241
+ b.left.parent = b
242
+ end
243
+ a.right = c.left
244
+ if a.right.present?
245
+ a.right.parent = a
246
+ end
247
+ c.right = b
248
+ b.parent = c
249
+ c.left = a
250
+ a.parent = c
251
+ if f.nil?
252
+ @root = c
253
+ @root.parent = nil
254
+ else
255
+ if f.right == a
256
+ f.right = c
257
+ else
258
+ f.left = c
259
+ end
260
+ c.parent = f
261
+ end
262
+ recompute_heights a
263
+ recompute_heights b
264
+ end
265
+ else
266
+ assert node_to_rebalance.balance_factor == 2
267
+ if node_to_rebalance.left.balance_factor >= 0
268
+ b = a.left
269
+ c = b.left
270
+ # """Rebalance, case LLC """
271
+ assert a.present? && b.present? && c.present?
272
+ a.left = b.right
273
+ if a.left
274
+ a.left.parent = a
275
+ end
276
+ b.right = a
277
+ a.parent = b
278
+ if f.nil?
279
+ @root = b
280
+ @root.parent = nil
281
+ else
282
+ if f.right == a
283
+ f.right = b
284
+ else
285
+ f.left = b
286
+ end
287
+ b.parent = f
288
+ end
289
+ recompute_heights a
290
+ recompute_heights b.parent
291
+ else
292
+ b = a.left
293
+ c = b.right
294
+ # """Rebalance, case LRC """
295
+ assert a.present? && b.present? && c.present?
296
+ a.left = c.right
297
+ if a.left.present?
298
+ a.left.parent = a
299
+ end
300
+ b.right = c.left
301
+ if b.right.present?
302
+ b.right.parent = b
303
+ end
304
+ c.left = b
305
+ b.parent = c
306
+ c.right = a
307
+ a.parent = c
308
+ if f.nil?
309
+ @root = c
310
+ @root.parent = nil
311
+ else
312
+ if f.right == a
313
+ f.right = c
314
+ else
315
+ f.left = c
316
+ end
317
+ c.parent = f
318
+ end
319
+ recompute_heights a
320
+ recompute_heights b
321
+ end
322
+ end
323
+ end
324
+
325
+ def delete node_or_key
326
+ if BinaryNode == node_or_key.class
327
+ node = node_or_key
328
+ else
329
+ node = find node_or_key
330
+ end
331
+
332
+ if node.present?
333
+ @size -= 1
334
+ invalidate_cached_values
335
+
336
+ # There are three cases:
337
+ #
338
+ # 1) The node is a leaf. Remove it and return.
339
+ #
340
+ # 2) The node is a branch (has only 1 child). Make the pointer to this node
341
+ # point to the child of this node.
342
+ #
343
+ # 3) The node has two children. Swap items with the successor
344
+ # of the node (the smallest item in its right subtree) and
345
+ # delete the successor from the right subtree of the node.
346
+ if node.is_leaf?
347
+ remove_leaf node
348
+ elsif node.left.present? ^ node.right.present?
349
+ remove_branch node
350
+ else
351
+ assert node.left.present? && node.right.present?
352
+ swap_with_successor_and_remove node
353
+ end
354
+ end
355
+ node
356
+ end
357
+
358
+ def remove_leaf node
359
+ parent = node.parent
360
+ if parent.present?
361
+ if parent.left == node
362
+ parent.left = nil
363
+ else
364
+ assert parent.right == node
365
+ parent.right = nil
366
+ end
367
+ recompute_heights parent
368
+ else
369
+ @root = nil
370
+ end
371
+ #del node
372
+ # rebalance
373
+ node = parent
374
+ while node.present?
375
+ rebalance node if node.balance_factor.abs > 1
376
+ node = node.parent
377
+ end
378
+ end
379
+
380
+
381
+ def remove_branch node
382
+ parent = node.parent
383
+ if parent
384
+ if parent.left == node
385
+ if node.right.present?
386
+ parent.left = node.right
387
+ else
388
+ parent.left = node.left
389
+ end
390
+ else
391
+ assert parent.right == node
392
+ if node.right.present?
393
+ parent.right = node.right
394
+ else
395
+ parent.right = node.left
396
+ end
397
+ end
398
+ if node.left
399
+ node.left.parent = parent
400
+ else
401
+ assert node.right.present?
402
+ node.right.parent = parent
403
+ end
404
+ recompute_heights parent
405
+
406
+ end
407
+
408
+ #del node
409
+ # rebalance
410
+ node = parent
411
+ while node.present?
412
+ rebalance node if node.balance_factor.abs > 1
413
+ node = node.parent
414
+ end
415
+ end
416
+
417
+ def swap_with_successor_and_remove node
418
+ successor = locate_min node.right
419
+ swap_nodes node, successor
420
+ assert node.left.nil?
421
+ if node.height == 0
422
+ remove_leaf node
423
+ else
424
+ remove_branch node
425
+ end
426
+ end
427
+
428
+ def swap_nodes node1, node2
429
+ assert node1.height > node2.height
430
+ parent1 = node1.parent
431
+ left_child1 = node1.left
432
+ right_child1 = node1.right
433
+ parent2 = node2.parent
434
+ assert parent2.present?
435
+ assert parent2.left == node2 || parent2 == node1
436
+ left_child2 = node2.left
437
+ assert left_child2.nil?
438
+ right_child2 = node2.right
439
+
440
+ # swap heights
441
+ tmp = node1.height
442
+ node1.height = node2.height
443
+ node2.height = tmp
444
+
445
+ if parent1
446
+ if parent1.left == node1
447
+ parent1.left = node2
448
+ else
449
+ assert parent1.right == node1
450
+ parent1.right = node2
451
+ end
452
+ node2.parent = parent1
453
+ else
454
+ @root = node2
455
+ @root.parent = nil
456
+ end
457
+
458
+ node2.left = left_child1
459
+ left_child1.parent = node2
460
+ node1.left = left_child2 # None
461
+ node1.right = right_child2
462
+ if right_child2
463
+ right_child2.parent = node1
464
+ end
465
+ if parent2 != node1
466
+ node2.right = right_child1
467
+ right_child1.parent = node2
468
+
469
+ parent2.left = node1
470
+ node1.parent = parent2
471
+ else
472
+ node2.right = node1
473
+ node1.parent = node2
474
+ end
475
+ end
476
+
477
+
478
+ end
479
+
480
+ require 'binary_search_tree_hash.rb'
@@ -0,0 +1,188 @@
1
+ class BinarySearchTreeHash
2
+ include Enumerable
3
+
4
+ def initialize logger=nil
5
+ @bst = BinarySearchTree.new logger
6
+ end
7
+
8
+ def clear
9
+ @bst.clear
10
+ self
11
+ end
12
+
13
+ def empty?
14
+ @bst.empty?
15
+ end
16
+
17
+ def [] key
18
+ node = @bst.find key
19
+ node.present?? node.value : nil
20
+ end
21
+
22
+ def []=(key, value)
23
+ @bst.insert key, value
24
+ end
25
+
26
+ def delete key
27
+ @bst.remove key
28
+ self
29
+ end
30
+
31
+ def to_hash
32
+ h = {}
33
+ each{ |key, value| h[key] = value }
34
+ h
35
+ end
36
+ alias :to_h :to_hash
37
+
38
+ def keys
39
+ map{ |node| node.first }
40
+ end
41
+
42
+ def values
43
+ map{ |node| node.second }
44
+ end
45
+
46
+ def to_a
47
+ map{ |node| [node.first, node.second] }
48
+ end
49
+
50
+ def to_s
51
+ '{' + map{ |node| "#{node.first} => #{node.second}" }.join(', ') + '}'
52
+ end
53
+
54
+ def min
55
+ [@bst.min.key, @bst.min.value]
56
+ end
57
+
58
+ def max
59
+ [@bst.max.key, @bst.max.value]
60
+ end
61
+
62
+ def each
63
+ @bst.nodes.each { |node| yield [node.key, node.value] }
64
+ end
65
+
66
+ def each_key
67
+ @bst.nodes.each{ |node| yield node.key }
68
+ end
69
+
70
+ def each_value
71
+ @bst.nodes.each{ |node| yield node.value }
72
+ end
73
+
74
+ def each_pair
75
+ @bst.nodes.each{ |node| yield node.key, node.value }
76
+ end
77
+
78
+ def select
79
+ @bst.nodes.select{ |node| yield node.key, node.value }.map{ |node| [node.key, node.value] }
80
+ end
81
+
82
+ def reject
83
+ @bst.nodes.reject{ |node| yield node.key, node.value }.map{ |node| [node.key, node.value] }
84
+ end
85
+
86
+ def reject!
87
+ changed = false
88
+ nodes = @bst.nodes
89
+ nodes.each_with_index do |node, i|
90
+ if yield node.key, node.value
91
+ changed = true
92
+ @bst.remove node
93
+ nodes[i] = nil
94
+ end
95
+ end
96
+ changed ? nodes.compact.map{ |node| [node.key, node.value] } : nil
97
+ end
98
+
99
+ def delete_if
100
+ nodes = @bst.nodes
101
+ nodes.each_with_index do |node, i|
102
+ if yield node.key, node.value
103
+ @bst.remove node
104
+ nodes[i] = nil
105
+ end
106
+ end
107
+ nodes.compact.map{ |node| [node.key, node.value] }
108
+ end
109
+
110
+ def size
111
+ @bst.size
112
+ end
113
+ alias :length :size
114
+
115
+ def has_key? key
116
+ @bst.find(key).present?
117
+ end
118
+
119
+ def include? key
120
+ has_key? key
121
+ end
122
+
123
+ def key? key
124
+ has_key? key
125
+ end
126
+
127
+ def member? key
128
+ has_key? key
129
+ end
130
+
131
+ def has_value? value
132
+ @bst.find_value(value).present?
133
+ end
134
+
135
+ def value? value
136
+ has_value? value
137
+ end
138
+
139
+ def key value
140
+ @bst.find_value(value).key
141
+ end
142
+
143
+ def values_at key, *extra_keys
144
+ #TODO: optimize this so that only one tree traversal occurs
145
+ ([key] + extra_keys).map{ |key| @bst.find(key).value }
146
+ end
147
+
148
+ def shift
149
+ deleted_node = @bst.remove_min
150
+ deleted_node.present?? [deleted_node.key, deleted_node.value] : nil
151
+ end
152
+
153
+ def invert
154
+ inverted_bst_hash = BinarySearchTreeHash.new
155
+ each{ |key, value| inverted_bst_hash[value] = key }
156
+ inverted_bst_hash
157
+ end
158
+
159
+ def replace other_bst_hash
160
+ clear
161
+ merge! other_bst_hash
162
+ end
163
+
164
+ def merge other_bst_hash
165
+ merged_hash = BinarySearchTreeHash.new
166
+ each{ |key, value| merged_hash[key] = value}
167
+ other_bst_hash.each{ |key, value| merged_hash[key] = value }
168
+ merged_hash
169
+ end
170
+
171
+ def merge! other_bst_hash
172
+ other_bst_hash.each{ |key, value| self[key] = value }
173
+ self
174
+ end
175
+
176
+ def update other_bst_hash
177
+ merge! other_bst_hash
178
+ end
179
+
180
+ def == other_bst_hash
181
+ bst == other_bst_hash.send(:bst)
182
+ end
183
+
184
+ private
185
+ def bst
186
+ @bst
187
+ end
188
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: binary_search_tree
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: "1.2"
6
+ platform: ruby
7
+ authors:
8
+ - Misha Conway
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-03 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description:
18
+ email: MishaAConway@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - lib/binary_search_tree.rb
27
+ - lib/binary_search_tree_hash.rb
28
+ has_rdoc: true
29
+ homepage:
30
+ licenses: []
31
+
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: "0"
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ requirements: []
50
+
51
+ rubyforge_project: nowarning
52
+ rubygems_version: 1.6.1
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: A self balancing avl binary search tree class. Also includes BinarySearchTreeHash which is a hash like class that internally uses binary search tree.
56
+ test_files: []
57
+