binary_search_tree 2.0 → 2.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,42 @@
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 && right
17
+ [left.height, right.height].max
18
+ elsif left
19
+ left.height
20
+ elsif right
21
+ right.height
22
+ else
23
+ -1
24
+ end
25
+ end
26
+
27
+ def balance_factor
28
+ left_height = if left
29
+ left.height
30
+ else
31
+ -1
32
+ end
33
+
34
+ right_height = if right
35
+ right.height
36
+ else
37
+ -1
38
+ end
39
+
40
+ left_height - right_height
41
+ end
42
+ end
@@ -0,0 +1,493 @@
1
+ class BinarySearchTree
2
+ attr_reader :size, :root
3
+
4
+ def balanced?
5
+ if -1 == compute_and_check_height(@root)
6
+ false
7
+ else
8
+ true
9
+ end
10
+ end
11
+
12
+ def initialize logger=nil
13
+ @logger = logger
14
+ clear
15
+ end
16
+
17
+ def clear
18
+ @root = nil
19
+ @size = 0
20
+ end
21
+
22
+ def empty?
23
+ @root.nil?
24
+ end
25
+
26
+ def find key
27
+ @num_comparisons = 0
28
+ node = locate key, @root
29
+ @logger.debug "find operation completed in #{@num_comparisons} lookups..." if @logger
30
+ node
31
+ end
32
+
33
+ def find_value value
34
+ find_value_ex @root, value
35
+ end
36
+
37
+ def min
38
+ @min ||= locate_min @root
39
+ end
40
+
41
+ def max
42
+ @max ||= locate_max @root
43
+ end
44
+
45
+ def insert element, value
46
+ put element, value, @root, nil
47
+ end
48
+
49
+ def remove node_or_key
50
+ delete node_or_key
51
+ end
52
+
53
+ def remove_min
54
+ delete min
55
+ end
56
+
57
+ def nodes
58
+ @nodes = []
59
+ serialize_nodes @root
60
+ @nodes
61
+ end
62
+
63
+ def == other_bst
64
+ compare @root, other_bst.root
65
+ end
66
+
67
+ private
68
+ def invalidate_cached_values
69
+ @min = @max = nil
70
+ end
71
+
72
+ def locate target, leaf
73
+ @num_comparisons += 1
74
+ if leaf.nil?
75
+ return nil
76
+ elsif leaf.key < target
77
+ return locate target, leaf.right
78
+ elsif leaf.key > target
79
+ return locate target, leaf.left
80
+ elsif leaf.key == target
81
+ return leaf
82
+ end
83
+ end
84
+
85
+ def locate_min leaf
86
+ return nil if leaf.nil?
87
+ return leaf if leaf.left.nil?
88
+ return locate_min leaf.left
89
+ end
90
+
91
+ def locate_max leaf
92
+ return nil if leaf.nil?
93
+ return leaf if leaf.right.nil?
94
+ return locate_max leaf.right
95
+ end
96
+
97
+ def recompute_heights start_from_node
98
+ changed = true
99
+ node = start_from_node
100
+ while node && changed
101
+ old_height = node.height
102
+ if node.right || node.left
103
+ node.height = node.max_children_height + 1
104
+ else
105
+ node.height = 0
106
+ end
107
+ changed = node.height != old_height
108
+ node = node.parent
109
+ end
110
+ end
111
+
112
+ def put element, value, leaf, parent, link_type=nil
113
+ #once you reach a point where you can place a new node
114
+ if leaf.nil?
115
+ #create that new node
116
+ leaf = BinaryNode.new element, value, parent
117
+ @size += 1
118
+ invalidate_cached_values
119
+ if parent
120
+ if 'left' == link_type
121
+ parent.left = leaf
122
+ else
123
+ parent.right = leaf
124
+ end
125
+ else
126
+ @root = leaf
127
+ end
128
+ if parent && parent.height.zero?
129
+ #if it has a parent but it is balanced, move up
130
+ node = parent
131
+ node_to_rebalance = nil
132
+
133
+ #continue moving up until you git the root
134
+ while node
135
+ node.height = node.max_children_height + 1
136
+ if node.balance_factor.abs > 1
137
+ node_to_rebalance = node
138
+ break
139
+ end
140
+ node = node.parent
141
+ end
142
+ #if at any point you reach an unbalanced node, rebalance it
143
+ rebalance node_to_rebalance if node_to_rebalance
144
+ end
145
+
146
+ elsif leaf.key < element
147
+ put element, value, leaf.right, leaf, "right"
148
+ elsif leaf.key > element
149
+ put element, value, leaf.left, leaf, "left"
150
+ elsif leaf.key == element
151
+ leaf.value = value
152
+ end
153
+ end
154
+
155
+ def find_value_ex leaf, value
156
+ if leaf
157
+ node_with_value = find_value_ex leaf.left, value
158
+ return node_with_value if node_with_value
159
+ return leaf if leaf.value == value
160
+ node_with_value = find_value_ex leaf.right, value
161
+ return node_with_value if node_with_value
162
+ end
163
+ nil
164
+ end
165
+
166
+ def serialize_nodes leaf
167
+ unless leaf.nil?
168
+ serialize_nodes leaf.left
169
+ @nodes << leaf
170
+ serialize_nodes leaf.right
171
+ end
172
+ end
173
+
174
+ def compare leaf, other_bst_leaf
175
+ if leaf && other_bst_leaf
176
+ leaf.value == other_bst_leaf.value &&
177
+ compare(leaf.left, other_bst_leaf.left) &&
178
+ compare(leaf.right, other_bst_leaf.right)
179
+ else
180
+ leaf.nil? && other_bst_leaf.nil?
181
+ end
182
+ end
183
+
184
+ def assert condition
185
+ raise "assertion failed" unless condition
186
+ end
187
+
188
+ def rrc_rebalance a, f
189
+ b = a.right
190
+ c = b.right
191
+ assert a && b && c
192
+ a.right = b.left
193
+ if a.right
194
+ a.right.parent = a
195
+ end
196
+ b.left = a
197
+ a.parent = b
198
+ if f.nil?
199
+ @root = b
200
+ @root.parent = nil
201
+ else
202
+ if f.right == a
203
+ f.right = b
204
+ else
205
+ f.left = b
206
+ end
207
+ b.parent = f
208
+ end
209
+ recompute_heights a
210
+ recompute_heights b.parent
211
+ end
212
+
213
+ def rlc_rebalance a, f
214
+ b = a.right
215
+ c = b.left
216
+ assert a && b && c
217
+ b.left = c.right
218
+ if b.left
219
+ b.left.parent = b
220
+ end
221
+ a.right = c.left
222
+ if a.right
223
+ a.right.parent = a
224
+ end
225
+ c.right = b
226
+ b.parent = c
227
+ c.left = a
228
+ a.parent = c
229
+ if f.nil?
230
+ @root = c
231
+ @root.parent = nil
232
+ else
233
+ if f.right == a
234
+ f.right = c
235
+ else
236
+ f.left = c
237
+ end
238
+ c.parent = f
239
+ end
240
+ recompute_heights a
241
+ recompute_heights b
242
+ end
243
+
244
+ def llc_rebalance a, b, c, f
245
+ assert a && b && c
246
+ a.left = b.right
247
+ if a.left
248
+ a.left.parent = a
249
+ end
250
+ b.right = a
251
+ a.parent = b
252
+ if f.nil?
253
+ @root = b
254
+ @root.parent = nil
255
+ else
256
+ if f.right == a
257
+ f.right = b
258
+ else
259
+ f.left = b
260
+ end
261
+ b.parent = f
262
+ end
263
+ recompute_heights a
264
+ recompute_heights b.parent
265
+ end
266
+
267
+ def lrc_rebalance a, b, c, f
268
+ assert a && b && c
269
+ a.left = c.right
270
+ if a.left
271
+ a.left.parent = a
272
+ end
273
+ b.right = c.left
274
+ if b.right
275
+ b.right.parent = b
276
+ end
277
+ c.left = b
278
+ b.parent = c
279
+ c.right = a
280
+ a.parent = c
281
+ if f.nil?
282
+ @root = c
283
+ @root.parent = nil
284
+ else
285
+ if f.right == a
286
+ f.right = c
287
+ else
288
+ f.left = c
289
+ end
290
+ c.parent = f
291
+ end
292
+ recompute_heights a
293
+ recompute_heights b
294
+ end
295
+
296
+ def rebalance node_to_rebalance
297
+ a = node_to_rebalance
298
+ f = a.parent #allowed to be NULL
299
+ if node_to_rebalance.balance_factor == -2
300
+ if node_to_rebalance.right.balance_factor <= 0
301
+ # """Rebalance, case RRC """
302
+ rrc_rebalance a, f
303
+ else
304
+ rlc_rebalance a, f
305
+ # """Rebalance, case RLC """
306
+ end
307
+ else
308
+ assert node_to_rebalance.balance_factor == 2
309
+ if node_to_rebalance.left.balance_factor >= 0
310
+ b = a.left
311
+ c = b.left
312
+ # """Rebalance, case LLC """
313
+ llc_rebalance a, b, c, f
314
+ else
315
+ b = a.left
316
+ c = b.right
317
+ # """Rebalance, case LRC """
318
+ lrc_rebalance a, b, c, f
319
+ end
320
+ end
321
+ end
322
+
323
+ def delete node_or_key
324
+ if BinaryNode == node_or_key.class
325
+ node = node_or_key
326
+ else
327
+ node = find node_or_key
328
+ end
329
+
330
+ if node
331
+ @size -= 1
332
+ invalidate_cached_values
333
+
334
+ # There are three cases:
335
+ #
336
+ # 1) The node is a leaf. Remove it and return.
337
+ #
338
+ # 2) The node is a branch (has only 1 child). Make the pointer to this node
339
+ # point to the child of this node.
340
+ #
341
+ # 3) The node has two children. Swap items with the successor
342
+ # of the node (the smallest item in its right subtree) and
343
+ # delete the successor from the right subtree of the node.
344
+ if node.is_leaf?
345
+ remove_leaf node
346
+ elsif (!!node.left) ^ (!!node.right)
347
+ remove_branch node
348
+ else
349
+ assert node.left && node.right
350
+ swap_with_successor_and_remove node
351
+ end
352
+ end
353
+ node
354
+ end
355
+
356
+ def remove_leaf node
357
+ parent = node.parent
358
+ if parent
359
+ if parent.left == node
360
+ parent.left = nil
361
+ else
362
+ assert parent.right == node
363
+ parent.right = nil
364
+ end
365
+ recompute_heights parent
366
+ else
367
+ @root = nil
368
+ end
369
+ #del node
370
+ # rebalance
371
+ node = parent
372
+ while node
373
+ rebalance node unless [-1, 0, 1].include? node.balance_factor
374
+ node = node.parent
375
+ end
376
+ end
377
+
378
+
379
+
380
+
381
+ def remove_branch node
382
+ parent = node.parent
383
+ if parent
384
+ if parent.left == node
385
+ parent.left = node.right || node.left
386
+ else
387
+ assert parent.right == node
388
+ parent.right = node.right || node.left
389
+ end
390
+ if node.left
391
+ node.left.parent = parent
392
+ else
393
+ assert node.right
394
+ node.right.parent = parent
395
+ end
396
+ recompute_heights parent
397
+ else
398
+ if node.left
399
+ @root = node.left
400
+ node.left.parent = nil
401
+ else
402
+ @root = node.right
403
+ node.right.parent = nil
404
+ end
405
+ recompute_heights @root
406
+ end
407
+
408
+ # rebalance
409
+ node = parent
410
+ while node
411
+ rebalance node unless [-1,0,1].include? node.balance_factor
412
+ node = node.parent
413
+ end
414
+ end
415
+
416
+ def swap_with_successor_and_remove node
417
+ successor = locate_min node.right
418
+ swap_nodes node, successor
419
+ assert node.left.nil?
420
+ if node.height == 0
421
+ remove_leaf node
422
+ else
423
+ remove_branch node
424
+ end
425
+ end
426
+
427
+ def swap_nodes node1, node2
428
+ assert node1.height > node2.height
429
+ parent1 = node1.parent
430
+ left_child1 = node1.left
431
+ right_child1 = node1.right
432
+ parent2 = node2.parent
433
+ assert parent2
434
+ assert parent2.left == node2 || parent2 == node1
435
+ left_child2 = node2.left
436
+ assert left_child2.nil?
437
+ right_child2 = node2.right
438
+
439
+ # swap heights
440
+ tmp = node1.height
441
+ node1.height = node2.height
442
+ node2.height = tmp
443
+
444
+ if parent1
445
+ if parent1.left == node1
446
+ parent1.left = node2
447
+ else
448
+ assert parent1.right == node1
449
+ parent1.right = node2
450
+ end
451
+ node2.parent = parent1
452
+ else
453
+ @root = node2
454
+ @root.parent = nil
455
+ end
456
+
457
+ node2.left = left_child1
458
+ left_child1.parent = node2
459
+ node1.left = left_child2 # None
460
+ node1.right = right_child2
461
+ if right_child2
462
+ right_child2.parent = node1
463
+ end
464
+ if parent2 != node1
465
+ node2.right = right_child1
466
+ right_child1.parent = node2
467
+
468
+ parent2.left = node1
469
+ node1.parent = parent2
470
+ else
471
+ node2.right = node1
472
+ node1.parent = node2
473
+ end
474
+ end
475
+
476
+ def compute_and_check_height root
477
+ return 0 if root.nil?
478
+ left_sub_tree_height = compute_and_check_height root.left
479
+ return -1 if -1 == left_sub_tree_height
480
+
481
+ right_sub_tree_height = compute_and_check_height root.right
482
+ return -1 if -1 == right_sub_tree_height
483
+
484
+ height_difference = (left_sub_tree_height - right_sub_tree_height).abs;
485
+
486
+ if height_difference > 1
487
+ -1
488
+ else
489
+ [left_sub_tree_height, right_sub_tree_height].max + 1
490
+ end
491
+ end
492
+ end
493
+