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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 03a236331048d1d421274964c9bb1f786a2433f8
4
- data.tar.gz: dc1468a43def290f733104a9df3cd23966c80dd5
3
+ metadata.gz: 1146c21c38df640ed4d1660d3628ac131b045f73
4
+ data.tar.gz: 17f900353706322d84eb7d392223350914b65895
5
5
  SHA512:
6
- metadata.gz: 3809ba134072969efddf39f35284abaf407680821c17eab8932d7b05133ef651eb81ae6ff78793a99fe11c28a703f2b8af892305098a7e499e807f5d21d271e6
7
- data.tar.gz: 3edbf53d2331c7629d2aa4dbffac2930ec9aa99a16786cf34a7888f4af1380e7fd53eb947f026da982062cfb6fdb1b378e39490176ad7a1b93e5fca96b40bff5
6
+ metadata.gz: c5938628cedda9f0d9292c54fd5f45e93df32a2bcb226e5ec078c5dd53f79a76e9893423a2e26d9dbac9ea9ed6faff7dd4464f05e73974cf97f2270114b65ec7
7
+ data.tar.gz: 90bb4aff76e0097329279cd832a1af1f6b936a1a7be34e3f3ea72be5c13b58a900dcf3a7edbb0bd8cea542671ea774673f76fa450978d91bdd8e42000a043bae
@@ -0,0 +1 @@
1
+ *.swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in conversocial.gemspec
4
+ gemspec
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ binary_search_tree (2.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rspec (3.4.0)
11
+ rspec-core (~> 3.4.0)
12
+ rspec-expectations (~> 3.4.0)
13
+ rspec-mocks (~> 3.4.0)
14
+ rspec-core (3.4.1)
15
+ rspec-support (~> 3.4.0)
16
+ rspec-expectations (3.4.0)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.4.0)
19
+ rspec-mocks (3.4.1)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.4.0)
22
+ rspec-support (3.4.1)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ binary_search_tree!
29
+ rspec
30
+
31
+ BUNDLED WITH
32
+ 1.10.6
data/README ADDED
@@ -0,0 +1,76 @@
1
+ Installation:
2
+ gem install binary_search_tree
3
+
4
+ Info:
5
+ This gem implements the two classes BinarySearchTree and BinarySearchTreeHash. BinarySearchTree is a self balancing avl tree.
6
+
7
+ Most people will only need to be concerned with BinarySearchTreeHash as it is a wrapper over BinarySearchTree that provides the same interface as the native Ruby hash. This class is useful when you want a container that provides fast lookups with minimal memory footprint. Since it is self balancing, it will reorganize itself on every new insert to make subsequent lookups optimal.
8
+
9
+ -----------------------------------------
10
+ -----------------------------------------
11
+ --Example usage of BinarySearchTreeHash--
12
+ -----------------------------------------
13
+ -----------------------------------------
14
+ ruby-1.9.2> h = BinarySearchTreeHash.new logger (note: passing a logger is optional)
15
+ => {}
16
+ ruby-1.9.2> (1..100).each{|i| h[i] = i*1000}
17
+ ruby-1.9.2> h[45]
18
+ DEBUG - [03/May/2011 15:02:07] "find operation completed in 7 lookups..."
19
+ => 45000
20
+ ruby-1.9.2> h[77]
21
+ DEBUG - [03/May/2011 15:02:14] "find operation completed in 6 lookups..."
22
+ => 77000
23
+ ruby-1.9.2> h[32]
24
+ DEBUG - [03/May/2011 15:02:18] "find operation completed in 2 lookups..."
25
+ => 32000
26
+ ruby-1.9.2> h.delete 32
27
+ DEBUG - [03/May/2011 15:02:29] "find operation completed in 2 lookups..."
28
+ ruby-1.9.2> h[32]
29
+ DEBUG - [03/May/2011 15:02:58] "find operation completed in 8 lookups..."
30
+ => nil
31
+ ruby-1.9.2> h[5000] = 7777
32
+ => 7777
33
+ ruby-1.9.2> h[5000]
34
+ DEBUG - [03/May/2011 15:03:33] "find operation completed in 7 lookups..."
35
+ => 7777
36
+ ruby-1.9.2> h.size
37
+ => 100
38
+ ruby-1.9.2> h.delete 50
39
+ DEBUG - [03/May/2011 15:03:48] "find operation completed in 6 lookups..."
40
+ ruby-1.9.2> h.size
41
+ => 99
42
+
43
+ ---------------------------------------------------------------------------------------
44
+ ---------------------------------------------------------------------------------------
45
+ --Example usage of BinarySearchTree (only use this if you want something lower level)--
46
+ ---------------------------------------------------------------------------------------
47
+ ---------------------------------------------------------------------------------------
48
+ ruby-1.9.2> b = BinarySearchTree.new logger (note: passing a logger is optional)
49
+ ruby-1.9.2> b.insert 45, 78
50
+ ruby-1.9.2> b.insert 23, 5
51
+ ruby-1.9.2> b.insert 77, 999
52
+ ruby-1.9.2> b.insert 43, 999
53
+ ruby-1.9.2> b.find(23).value
54
+ DEBUG - [03/May/2011 15:23:15] "find operation completed in 2 lookups..."
55
+ => 5
56
+ ruby-1.9.2> b.find(24)
57
+ DEBUG - [03/May/2011 15:23:32] "find operation completed in 4 lookups..."
58
+ => nil
59
+ ruby-1.9.2> b.find(43).value
60
+ DEBUG - [03/May/2011 15:23:40] "find operation completed in 3 lookups..."
61
+ => 999
62
+ ruby-1.9.2> b.max.value
63
+ => 999
64
+ ruby-1.9.2> b.min.value
65
+ => 5
66
+ ruby-1.9.2> b.size
67
+ => 4
68
+ ruby-1.9.2> b.remove 23
69
+ DEBUG - [03/May/2011 15:24:41] "find operation completed in 2 lookups..."
70
+ ruby-1.9.2> b.size
71
+ => 3
72
+ ruby-1.9.2> b.clear
73
+ => 0
74
+ ruby-1.9.2> b.size
75
+ => 0
76
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'binary_search_tree/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "binary_search_tree"
8
+ s.version = BinarySearch::VERSION
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.require_paths = ["lib"]
12
+ s.authors = ["Misha Conway"]
13
+ s.email = "MishaAConway@gmail.com"
14
+ s.files = `git ls-files -z`.split("\x0")
15
+ s.licenses = ["MIT"]
16
+ s.rubyforge_project = "nowarning"
17
+ s.rubygems_version = "2.4.5"
18
+ s.summary = "A self balancing avl binary search tree class. Also includes BinarySearchTreeHash which is a hash like class that internally uses binary search tree."
19
+ s.add_development_dependency "rspec"
20
+ end
@@ -1,522 +1,4 @@
1
- require 'active_support/core_ext/object/blank'
2
- class BinaryNode
3
- attr_accessor :height, :parent, :left, :right, :key, :value
4
-
5
- def initialize key, value, parent
6
- @key = key
7
- @value = value
8
- @parent = parent
9
- @height = 0
10
- end
11
-
12
- def is_leaf?
13
- height.zero?
14
- end
15
-
16
- def max_children_height
17
- if left.present? && right.present?
18
- [left.height, right.height].max
19
- elsif left.present?
20
- left.height
21
- elsif right.present?
22
- right.height
23
- else
24
- -1
25
- end
26
- end
27
-
28
- def balance_factor
29
- (left.height rescue -1) - (right.height rescue -1)
30
- end
31
- end
32
-
33
-
34
- class BinarySearchTree
35
- attr_reader :size, :root
36
-
37
- def initialize logger=nil
38
- @logger = logger
39
- clear
40
- end
41
-
42
- def clear
43
- @root = nil
44
- @size = 0
45
- end
46
-
47
- def empty?
48
- @root.nil?
49
- end
50
-
51
- def find key
52
- @num_comparisons = 0
53
- node = locate key, @root
54
- @logger.debug "find operation completed in #{@num_comparisons} lookups..." if @logger.present?
55
- node
56
- end
57
-
58
- def find_value value
59
- find_value_ex @root, value
60
- end
61
-
62
- def min
63
- @min ||= locate_min @root
64
- end
65
-
66
- def max
67
- @max ||= locate_max @root
68
- end
69
-
70
- def insert element, value
71
- put element, value, @root, nil
72
- end
73
-
74
- def remove node_or_key
75
- delete node_or_key
76
- end
77
-
78
- def remove_min
79
- delete min
80
- end
81
-
82
- def nodes
83
- @nodes = []
84
- serialize_nodes @root
85
- @nodes
86
- end
87
-
88
- def == other_bst
89
- compare @root, other_bst.root
90
- end
91
-
92
- private
93
- def invalidate_cached_values
94
- @min = @max = nil
95
- end
96
-
97
- def locate target, leaf
98
- @num_comparisons += 1
99
- if leaf.nil?
100
- return nil
101
- elsif leaf.key < target
102
- return locate target, leaf.right
103
- elsif leaf.key > target
104
- return locate target, leaf.left
105
- elsif leaf.key == target
106
- return leaf
107
- end
108
- end
109
-
110
- def locate_min leaf
111
- return nil if leaf.nil?
112
- return leaf if leaf.left.nil?
113
- return locate_min leaf.left
114
- end
115
-
116
- def locate_max leaf
117
- return nil if leaf.nil?
118
- return leaf if leaf.right.nil?
119
- return locate_max leaf.right
120
- end
121
-
122
- def recompute_heights start_from_node
123
- changed = true
124
- node = start_from_node
125
- while node && changed
126
- old_height = node.height
127
- if node.right.present? || node.left.present?
128
- node.height = node.max_children_height + 1
129
- else
130
- node.height = 0
131
- end
132
- changed = node.height != old_height
133
- node = node.parent
134
- end
135
- end
136
-
137
- def put element, value, leaf, parent, link_type=nil
138
- #once you reach a point where you can place a new node
139
- if leaf.nil?
140
- #create that new node
141
- leaf = BinaryNode.new element, value, parent
142
- @size += 1
143
- invalidate_cached_values
144
- if parent.present?
145
- if 'left' == link_type
146
- parent.left = leaf
147
- else
148
- parent.right = leaf
149
- end
150
- else
151
- @root = leaf
152
- end
153
- if parent.present? && parent.height.zero?
154
- #if it has a parent but it is balanced, move up
155
- node = parent
156
- node_to_rebalance = nil
157
-
158
- #continue moving up until you git the root
159
- while node.present?
160
- node.height = node.max_children_height + 1
161
- if node.balance_factor.abs > 1
162
- node_to_rebalance = node
163
- break
164
- end
165
- node = node.parent
166
- end
167
- #if at any point you reach an unbalanced node, rebalance it
168
- rebalance node_to_rebalance if node_to_rebalance.present?
169
- end
170
-
171
- elsif leaf.key < element
172
- put element, value, leaf.right, leaf, "right"
173
- elsif leaf.key > element
174
- put element, value, leaf.left, leaf, "left"
175
- elsif leaf.key == element
176
- leaf.value = value
177
- end
178
- end
179
-
180
- def find_value_ex leaf, value
181
- if leaf.present?
182
- node_with_value = find_value_ex leaf.left, value
183
- return node_with_value if node_with_value.present?
184
- return leaf if leaf.value == value
185
- node_with_value = find_value_ex leaf.right, value
186
- return node_with_value if node_with_value.present?
187
- end
188
- nil
189
- end
190
-
191
- def serialize_nodes leaf
192
- if !leaf.nil?
193
- serialize_nodes leaf.left
194
- @nodes += [leaf]
195
- serialize_nodes leaf.right
196
- end
197
- end
198
-
199
- def compare leaf, other_bst_leaf
200
- if leaf.present? && other_bst_leaf.present?
201
- leaf.value == other_bst_leaf.value &&
202
- compare(leaf.left, other_bst_leaf.left) &&
203
- compare(leaf.right, other_bst_leaf.right)
204
- else
205
- leaf.nil? && other_bst_leaf.nil?
206
- end
207
- end
208
-
209
- def assert condition
210
- raise "assertion failed" unless condition
211
- end
212
-
213
- def rrc_rebalance a, f
214
-
215
- #puts "performing rrc rebalance"
216
- b = a.right
217
- c = b.right
218
- assert a.present? && b.present? && c.present?
219
- a.right = b.left
220
- if a.right.present?
221
- a.right.parent = a
222
- end
223
- b.left = a
224
- a.parent = b
225
- if f.nil?
226
- @root = b
227
- @root.parent = nil
228
- else
229
- if f.right == a
230
- f.right = b
231
- else
232
- f.left = b
233
- end
234
- b.parent = f
235
- end
236
- recompute_heights a
237
- recompute_heights b.parent
238
- end
239
-
240
- def rlc_rebalance a, f
241
-
242
- #puts "performing rlc rebalance"
243
- b = a.right
244
- c = b.left
245
- assert a.present? && b.present? && c.present?
246
- b.left = c.right
247
- if b.left.present?
248
- b.left.parent = b
249
- end
250
- a.right = c.left
251
- if a.right.present?
252
- a.right.parent = a
253
- end
254
- c.right = b
255
- b.parent = c
256
- c.left = a
257
- a.parent = c
258
- if f.nil?
259
- @root = c
260
- @root.parent = nil
261
- else
262
- if f.right == a
263
- f.right = c
264
- else
265
- f.left = c
266
- end
267
- c.parent = f
268
- end
269
- recompute_heights a
270
- recompute_heights b
271
- end
272
-
273
- def llc_rebalance a, b, c, f
274
- #puts "performing llc rebalance"
275
- assert a.present? && b.present? && c.present?
276
- a.left = b.right
277
- if a.left
278
- a.left.parent = a
279
- end
280
- b.right = a
281
- a.parent = b
282
- if f.nil?
283
- @root = b
284
- @root.parent = nil
285
- else
286
- if f.right == a
287
- f.right = b
288
- else
289
- f.left = b
290
- end
291
- b.parent = f
292
- end
293
- recompute_heights a
294
- recompute_heights b.parent
295
- end
296
-
297
- def lrc_rebalance a, b, c, f
298
- #puts "performing lrc rebalance"
299
- assert a.present? && b.present? && c.present?
300
- a.left = c.right
301
- if a.left.present?
302
- a.left.parent = a
303
- end
304
- b.right = c.left
305
- if b.right.present?
306
- b.right.parent = b
307
- end
308
- c.left = b
309
- b.parent = c
310
- c.right = a
311
- a.parent = c
312
- if f.nil?
313
- @root = c
314
- @root.parent = nil
315
- else
316
- if f.right == a
317
- f.right = c
318
- else
319
- f.left = c
320
- end
321
- c.parent = f
322
- end
323
- recompute_heights a
324
- recompute_heights b
325
- end
326
-
327
- def rebalance node_to_rebalance
328
- a = node_to_rebalance
329
- f = a.parent #allowed to be NULL
330
- if node_to_rebalance.balance_factor == -2
331
- if node_to_rebalance.right.balance_factor <= 0
332
- # """Rebalance, case RRC """
333
- rrc_rebalance a, f
334
- else
335
- rlc_rebalance a, f
336
- # """Rebalance, case RLC """
337
- end
338
- else
339
- assert node_to_rebalance.balance_factor == 2
340
- if node_to_rebalance.left.balance_factor >= 0
341
- b = a.left
342
- c = b.left
343
- # """Rebalance, case LLC """
344
- llc_rebalance a, b, c, f
345
- else
346
- b = a.left
347
- c = b.right
348
- # """Rebalance, case LRC """
349
- lrc_rebalance a, b, c, f
350
- end
351
- end
352
- end
353
-
354
- def delete node_or_key
355
- if BinaryNode == node_or_key.class
356
- node = node_or_key
357
- else
358
- node = find node_or_key
359
- end
360
-
361
- if node.present?
362
- @size -= 1
363
- invalidate_cached_values
364
-
365
- # There are three cases:
366
- #
367
- # 1) The node is a leaf. Remove it and return.
368
- #
369
- # 2) The node is a branch (has only 1 child). Make the pointer to this node
370
- # point to the child of this node.
371
- #
372
- # 3) The node has two children. Swap items with the successor
373
- # of the node (the smallest item in its right subtree) and
374
- # delete the successor from the right subtree of the node.
375
- if node.is_leaf?
376
- remove_leaf node
377
- elsif node.left.present? ^ node.right.present?
378
- remove_branch node
379
- else
380
- assert node.left.present? && node.right.present?
381
- swap_with_successor_and_remove node
382
- end
383
- end
384
- node
385
- end
386
-
387
- def remove_leaf node
388
- parent = node.parent
389
- if parent.present?
390
- if parent.left == node
391
- parent.left = nil
392
- else
393
- assert parent.right == node
394
- parent.right = nil
395
- end
396
- recompute_heights parent
397
- else
398
- @root = nil
399
- end
400
- #del node
401
- # rebalance
402
- node = parent
403
- while node.present?
404
- rebalance node if node.balance_factor.abs > 1
405
- node = node.parent
406
- end
407
- end
408
-
409
-
410
- def remove_branch node
411
- parent = node.parent
412
- if parent
413
- if parent.left == node
414
- if node.right.present?
415
- parent.left = node.right
416
- else
417
- parent.left = node.left
418
- end
419
- else
420
- assert parent.right == node
421
- if node.right.present?
422
- parent.right = node.right
423
- else
424
- parent.right = node.left
425
- end
426
- end
427
- if node.left
428
- node.left.parent = parent
429
- else
430
- assert node.right.present?
431
- node.right.parent = parent
432
- end
433
- recompute_heights parent
434
-
435
- end
436
-
437
- #del node
438
- # rebalance
439
- node = parent
440
- while node.present?
441
- rebalance node if node.balance_factor.abs > 1
442
- node = node.parent
443
- end
444
- end
445
-
446
- def swap_with_successor_and_remove node
447
- successor = locate_min node.right
448
- swap_nodes node, successor
449
- assert node.left.nil?
450
- if node.height == 0
451
- remove_leaf node
452
- else
453
- remove_branch node
454
- end
455
- end
456
-
457
- def swap_nodes node1, node2
458
- assert node1.height > node2.height
459
- parent1 = node1.parent
460
- left_child1 = node1.left
461
- right_child1 = node1.right
462
- parent2 = node2.parent
463
- assert parent2.present?
464
- assert parent2.left == node2 || parent2 == node1
465
- left_child2 = node2.left
466
- assert left_child2.nil?
467
- right_child2 = node2.right
468
-
469
- # swap heights
470
- tmp = node1.height
471
- node1.height = node2.height
472
- node2.height = tmp
473
-
474
- if parent1
475
- if parent1.left == node1
476
- parent1.left = node2
477
- else
478
- assert parent1.right == node1
479
- parent1.right = node2
480
- end
481
- node2.parent = parent1
482
- else
483
- @root = node2
484
- @root.parent = nil
485
- end
486
-
487
- node2.left = left_child1
488
- left_child1.parent = node2
489
- node1.left = left_child2 # None
490
- node1.right = right_child2
491
- if right_child2
492
- right_child2.parent = node1
493
- end
494
- if parent2 != node1
495
- node2.right = right_child1
496
- right_child1.parent = node2
497
-
498
- parent2.left = node1
499
- node1.parent = parent2
500
- else
501
- node2.right = node1
502
- node1.parent = node2
503
- end
504
- end
505
-
506
-
507
- end
508
-
509
- # #require 'binary_search_tree_hash.rb'
510
- # tree = BinarySearchTree.new
511
- # puts "inserting 9"
512
- # tree.insert(9, "nine")
513
- # puts "inserting 5"
514
- # tree.insert(5, "five")
515
- # puts "inserting 10"
516
- # tree.insert(10, "ten")
517
- # puts "inserting 1"
518
- # tree.insert(1, "ten")
519
- # puts "inserting 3"
520
- # tree.insert(3, "ten")
521
- # puts "inserting 7"
522
- # tree.insert(7, "ten")
1
+ require "binary_search_tree/version"
2
+ require 'binary_search_tree/binary_node'
3
+ require 'binary_search_tree/binary_search_tree'
4
+ require 'binary_search_tree/binary_search_tree_hash'