Class: Salgo::Btree
- Inherits:
-
Object
- Object
- Salgo::Btree
- Defined in:
- lib/salgo/btree.rb
Defined Under Namespace
Instance Attribute Summary collapse
-
#size ⇒ Object
readonly
Returns the value of attribute size.
Instance Method Summary collapse
-
#[]=(key, val) ⇒ Object
(also: #store)
Set the key in this tree to the given value.
- #delete(key) ⇒ Object
- #delete_key(key, node = @root) ⇒ Object
-
#delete_max_key(node = @root) ⇒ Object
Delete from a subtree.
-
#delete_min_key(node = @root) ⇒ Object
Delete from a subtree.
- #each(node = @root, &call) ⇒ Object
- #find(key) ⇒ Object (also: #[])
- #find_key(key) ⇒ Object
- #full?(node) ⇒ Boolean
-
#has_extra_keys?(node) ⇒ Boolean
Is there an extra key to take, should we need it.
- #has_key?(key) ⇒ Boolean (also: #member?, #include?, #key?)
- #has_minimum_keys?(node) ⇒ Boolean
-
#initialize(minnodes = 2) ⇒ Btree
constructor
A new instance of Btree.
-
#insert(key, val) ⇒ Object
Insert a new value at the given key.
-
#mergable?(node) ⇒ Boolean
Does the given node have enough keys (and perhaps nodes) to merge with another node?.
-
#merge_with_left(parent, child_index) ⇒ Object
Given the parent node and the child_index of a child node, this method will merge with the sibling to the left of the child.
-
#merge_with_right(parent, child_index) ⇒ Object
Same as merge_with_right, but the roles are reversed as are the locations of the resulting subtrees.
- #root?(node) ⇒ Boolean
- #split_root ⇒ Object
Constructor Details
Instance Attribute Details
#size ⇒ Object (readonly)
Returns the value of attribute size.
186 187 188 |
# File 'lib/salgo/btree.rb', line 186 def size @size end |
Instance Method Details
#[]=(key, val) ⇒ Object Also known as: store
Set the key in this tree to the given value. If there is already a value at the given key, it is replaced and the old value is returned. Nil is returned otherwise.
485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/salgo/btree.rb', line 485 def []=(key, val) k = find_key(Key.new(key)) if ( k.nil? ) insert(key, val) nil else v = k.val k.val = val v end end |
#delete(key) ⇒ Object
473 474 475 476 477 478 |
# File 'lib/salgo/btree.rb', line 473 def delete(key) key = delete_key(Key.new(key)) (key.nil?)? nil : key.val end |
#delete_key(key, node = @root) ⇒ Object
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 |
# File 'lib/salgo/btree.rb', line 410 def delete_key(key, node=@root) candidate, candidate_idx = node.find_node_or_key_containing_key(key) return nil if candidate.nil? # Delete from this node. if ( candidate.is_a?(Key) ) # If it's a simple delete... if node.leaf? node.keys.delete_at(candidate_idx) @size -= 1 return candidate elsif has_extra_keys?(node.nodes[candidate_idx]) node.keys[candidate_idx] = delete_max_key(node.nodes[candidate_idx]) return candidate elsif has_extra_keys?(node.nodes[candidate_idx+1]) node.keys[candidate_idx] = delete_min_key(node.nodes[candidate_idx+1]) return candidate else node = merge_with_right(node, candidate_idx) # The merge_with_right call left the root with no keys and 1 child node. # Replace the root and delete from the root. @root = @root.nodes[0] if ( @root.nodes.size == 1 ) return delete_key(key, node) end elsif candidate.is_a?(Node) # Ensure that the node can sustain a delete BEFORE entering it... unless has_extra_keys?(candidate) if ( node.first_node?(candidate) ) if ( has_extra_keys?(node.nodes[1])) another_key, another_node = node.nodes[1].take_min candidate.put_max(node.keys[0], another_node) node.keys[0] = another_key else merge_with_right(node, candidate_idx) end #elsif ( node.last_node?(candidate) ) else if ( has_extra_keys?(node.nodes[candidate_idx-1]) ) another_key, another_node = node.nodes[candidate_idx-1].take_max candidate.put_min(node.keys[candidate_idx-1], another_node) node.keys[candidate_idx-1] = another_key else merge_with_left(node, candidate_idx) end end end # If one of the above merges removed all keys from the root, then there is only 1 node. # Promote that node as the root. candidate = @root = @root.nodes[0] if ( @root.nodes.size == 1 ) delete_key(key, candidate) end end |
#delete_max_key(node = @root) ⇒ Object
Delete from a subtree. We assume the node can withstand delete when called. They key object is returned.
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
# File 'lib/salgo/btree.rb', line 330 def delete_max_key(node=@root) return nil if @size == 0 if root?(node) and @root.keys.size == 0 and @root.nodes.size == 1 @root = node = @root.nodes[0] end while(true) do if ( node.leaf? ) @size -= 1 return node.take_max()[0] else # Fix up the node before deleting from it. if has_minimum_keys?(node.nodes[-1]) if has_minimum_keys?(node.nodes[-2]) node = merge_with_left(node, node.nodes.size-1) else # Pull the max key and node from our "left" sibling. # Make the left key be our parent and put the node # in the minimum of the right tree node. another_key, another_node = node.nodes[-2].take_max node.nodes[-1].put_min(node.keys[-1], another_node) node.keys[-1] = another_key node = node.nodes[-1] end else node = node.nodes[-1] end end end end |
#delete_min_key(node = @root) ⇒ Object
Delete from a subtree. We assume the node can withstand delete when called. The key object is returned.
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
# File 'lib/salgo/btree.rb', line 372 def delete_min_key(node=@root) return nil if @size == 0 if root?(node) and @root.keys.size == 0 and @root.nodes.size == 1 @root = node = @root.nodes[0] end while(true) do if ( node.leaf? ) @size -= 1 return node.take_min()[0] else # Fix up the node before deleting from it. if has_minimum_keys?(node.nodes[0]) if has_minimum_keys?(node.nodes[1]) node = merge_with_right(node, 0) else # Pull the min key and node from our "right" sibling. # Make the right key be our parent and put the node # in the minimum of the right tree node. another_key, another_node = node.nodes[1].take_min node.nodes[0].put_max(node.keys[0], another_node) node.keys[0] = another_key node = node.nodes[0] end else node = node.nodes[0] end end end end |
#each(node = @root, &call) ⇒ Object
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
# File 'lib/salgo/btree.rb', line 499 def each(node=@root, &call) proc_child = ( node.leaf?() )? lambda { |x| } : lambda { |child_node| each(child_node, &call) } index = 0 node.keys.each do |key| proc_child.call(node.nodes[index]) call.call(key.key, key.val) index+=1 end proc_child.call(node.nodes[index]) end |
#find(key) ⇒ Object Also known as: []
286 287 288 289 290 |
# File 'lib/salgo/btree.rb', line 286 def find(key) k = find_key(Key.new(key, nil)) (k.nil?) ? nil : k.val end |
#find_key(key) ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/salgo/btree.rb', line 267 def find_key(key) node = @root candidate, candidate_idx = node.find_node_or_key_containing_key(key) while( ! candidate.nil?) if ( candidate.is_a?(Key) ) return candidate end node = candidate candidate, candidate_idx = node.find_node_or_key_containing_key(key) end return nil end |
#full?(node) ⇒ Boolean
199 200 201 |
# File 'lib/salgo/btree.rb', line 199 def full?(node) node.keys.size == @maxnodes-1 end |
#has_extra_keys?(node) ⇒ Boolean
Is there an extra key to take, should we need it.
213 214 215 |
# File 'lib/salgo/btree.rb', line 213 def has_extra_keys?(node) node.keys.size >= @minnodes end |
#has_key?(key) ⇒ Boolean Also known as: member?, include?, key?
518 519 520 |
# File 'lib/salgo/btree.rb', line 518 def has_key?(key) ! find_key(Key.new(key)).nil? end |
#has_minimum_keys?(node) ⇒ Boolean
208 209 210 |
# File 'lib/salgo/btree.rb', line 208 def has_minimum_keys?(node) node.keys.size < @minnodes end |
#insert(key, val) ⇒ Object
Insert a new value at the given key. Duplicate values are allowed in this data structure and insert does not prevent them. The []= method will replace values.
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/salgo/btree.rb', line 227 def insert(key, val) key = Key.new(key, val) # Always make sure our special friend "root" is OK and has room for an insert. split_root if full?(@root) parent_node = nil node = @root not_inserted = true while(not_inserted) if ( full? node ) median_key, lnode, rnode = node.split() # NOTE: Because we always split full root nodes, we will never enter here with parent_node=nil # Oh good, we can do a normal split and insert the result in the parent. parent_node.insert(median_key, lnode, rnode) if ( key < median_key ) node = lnode else node = rnode end end # Can we insert? if ( node.leaf? ) node.insert(key) @size += 1 not_inserted = false else # which node to examine? parent_node = node node = node.find_node_containing_key(key) end end end |
#mergable?(node) ⇒ Boolean
Does the given node have enough keys (and perhaps nodes) to merge with another node?
204 205 206 |
# File 'lib/salgo/btree.rb', line 204 def mergable?(node) node.keys.size < @minnodes end |
#merge_with_left(parent, child_index) ⇒ Object
Given the parent node and the child_index of a child node, this method will merge with the sibling to the left of the child. The resulting “unknown” tree will be placed on the left and the known-filled node will be placed in the child node’s current spot. The new target node is returned.
298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/salgo/btree.rb', line 298 def merge_with_left(parent, child_index) child = parent.nodes[child_index] sibling = parent.nodes[child_index-1] node = Node.new() node.nodes = sibling.nodes + child.nodes node.keys = sibling.keys + [ parent.keys[child_index-1] ] + child.keys parent.take(child_index-1) parent.nodes[child_index-1] = node node end |
#merge_with_right(parent, child_index) ⇒ Object
Same as merge_with_right, but the roles are reversed as are the locations of the resulting subtrees. The new target node is returned.
315 316 317 318 319 320 321 322 323 324 325 326 |
# File 'lib/salgo/btree.rb', line 315 def merge_with_right(parent, child_index) child = parent.nodes[child_index] sibling = parent.nodes[child_index+1] node = Node.new() node.nodes = child.nodes + sibling.nodes node.keys = child.keys + [ parent.keys[child_index] ] + sibling.keys parent.take(child_index) parent.nodes[child_index] = node node end |
#root?(node) ⇒ Boolean
195 196 197 |
# File 'lib/salgo/btree.rb', line 195 def root?(node) @root.equal? node end |