binary_search_tree 1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+