binary_search_tree 2.0 → 2.2

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