diff --git a/__main__.py b/__main__.py index b542589fb4f6b0342676d431cef25d9620645a3b..ab4fb51efed46d345af8780f9b12d2913ff2a50b 100644 --- a/__main__.py +++ b/__main__.py @@ -69,18 +69,6 @@ class Node: self.children = [] -def find_leaf(root, key): - parents = [] - current = root - - while not current.is_leaf: - parents.append(current) - children_index = lower_bound(current.keys, key) - current = current.children[children_index] - - return parents, current - - def node_is_full(node): return len(node.keys) == 2 * node.order @@ -191,8 +179,20 @@ def insert_non_full(node, key, previous_split_right_node): node.children.insert(inserted_at_index + 1, previous_split_right_node) +def insertion_find_leaf(root, key): + parents = [] + current = root + + while not current.is_leaf: + parents.append(current) + children_index = lower_bound(current.keys, key) + current = current.children[children_index] + + return parents, current + + def bptree_insert(root, key): - parents, leaf = find_leaf(root, key) + parents, leaf = insertion_find_leaf(root, key) if node_is_full(leaf): insert_full(root, parents, leaf, key, None) @@ -205,15 +205,80 @@ def bptree_insert(root, key): # DELETION : START -def bptree_delete(root, key): - parents, leaf = find_leaf(root, key) +def deletion_find_leaf(root, key): + parents = [] + current = root + + while not current.is_leaf: + parents.append(current) + children_index = lower_bound(current.keys, key) + + if children_index < len(current.keys) and current.keys[children_index] == key: + children_index += 1 + + current = current.children[children_index] + + return parents, current - if not is_value_in_array(leaf.keys, key): - return +def bptree_find_smallest_key(root): + if root.is_leaf: + return root.keys[0] + + return bptree_find_smallest_key(root.children[0]) + + +def bptree_shrink(root): + right_child = root.children[1] + root.keys = right_child.keys + root.children = right_child.children + + +def bptree_delete_internal(root, parents, node, key): + if node != root: + parent = parents.pop() + + if len(node.keys) < node.order and node != root: + children_index = None + + for i, child in enumerate(parent.children): + if node == child: + children_index = i + + if children_index == 0: + sibling = parent.children[children_index + 1] + parent_key = parent.keys[children_index] + parent.keys.pop(find_value_index_in_array(parent.keys, parent_key)) + array_insert_sorted(sibling.keys, parent_key) + array_insert_sorted(sibling.keys, node.keys[0]) + sibling.children = node.children + sibling.children + else: + sibling = parent.children[children_index - 1] + parent_key = parent.keys[children_index - 1] + parent.keys.pop(find_value_index_in_array(parent.keys, parent_key)) + array_insert_sorted(node.keys, parent_key) + + for key in sibling.keys: + array_insert_sorted(node.keys, key) + + node.children = sibling.children + node.children + + if parent == root and len(parent.keys) == 0: + bptree_shrink(root) + + if is_value_in_array(node.keys, key): + index = find_value_index_in_array(node.keys, key) + node.keys[index] = bptree_find_smallest_key(node.children[index + 1]) + + if node != root: + bptree_delete_internal(root, parents, parent, key) + + +def bptree_delete_leaf(root, parents, leaf, key): if len(leaf.keys) > leaf.order: deletion_index = find_value_index_in_array(leaf.keys, key) leaf.keys.pop(deletion_index) + bptree_delete_internal(root, parents, parents.pop(), key) else: deletion_index = find_value_index_in_array(leaf.keys, key) leaf.keys.pop(deletion_index) @@ -226,24 +291,72 @@ def bptree_delete(root, key): children_index = i if children_index == 0: - sibling = parent.children[children_index + 1] - else: + # Must take the sibling on the right. + sibling = parent.children[1] + sibling_position = "r" + elif children_index == 2 * leaf.order - 1: + # Must take the sibling on the left. sibling = parent.children[children_index - 1] - - if len(sibling.keys) == leaf.order: - pass + sibling_position = "l" else: - # Works only if children_index = 0 - borrowed_key = sibling.keys[0] - sibling.keys.pop(0) - leaf.keys.append(borrowed_key) - parent.keys[0] = sibling.keys[0] + # Can take the sibling from left or right. + if len(parent.children[children_index - 1].keys) > leaf.order: + # The left sibling has enough keys to borrow one. + sibling = parent.children[children_index - 1] + sibling_position = "l" + elif len(parent.children[children_index + 1].keys) > leaf.order: + # The right sibling has enough keys to borrow one. + sibling = parent.children[children_index + 1] + sibling_position = "r" + else: + # A merge between the node and the left sibling is required. + sibling = parent.children[children_index - 1] + + if len(sibling.keys) == sibling.order: + if children_index == 0: + # Sibling not on the left. + array_insert_sorted(sibling.keys, leaf.keys[0]) + parent.children.pop(children_index) + parent.keys.pop(children_index) + else: + for key in sibling.keys: + array_insert_sorted(leaf.keys, key) + + parent.children.pop(children_index - 1) + parent.keys.pop(children_index - 1) + + bptree_delete_internal(root, parents, parent, key) + else: + if sibling_position == "l": + sibling_key = sibling.keys[len(sibling.keys) - 1] + deletion_index = find_value_index_in_array(sibling.keys, sibling_key) + sibling.keys.pop(deletion_index) + array_insert_sorted(leaf.keys, sibling_key) + parent.keys[children_index - 1] = sibling_key + else: + sibling_key = sibling.keys[0] + deletion_index = find_value_index_in_array(sibling.keys, sibling_key) + sibling.keys.pop(deletion_index) + array_insert_sorted(leaf.keys, sibling_key) + parent.keys[children_index] = sibling.keys[0] + + bptree_delete_internal(root, parents, parent, key) + + +def bptree_delete(root, key): + parents, leaf = deletion_find_leaf(root, key) + + if not is_value_in_array(leaf.keys, key): + return + + bptree_delete_leaf(root, parents, leaf, key) # DELETION : END def bptree_search(root, key): + # BUG !!! if root.is_leaf: return is_value_in_array(root.keys, key) @@ -300,7 +413,8 @@ def generate_random_keys(length, min_key, max_key): def main(): - random.seed(0) + a = 2 + random.seed(a) order = 2 # root = Node(order) @@ -327,16 +441,23 @@ def main(): # assert not bptree_search(root, random_key) root = Node(order) - keys = generate_random_keys(8, 1, 99) + keys = generate_random_keys(18, 1, 99) print(keys) print("=====") for key in keys: bptree_insert(root, key) - bptree_delete(root, 51) - # bptree_delete(root, 52) - bptree_delete(root, 34) + bptree_delete(root, 56) + bptree_delete(root, 82) + bptree_delete(root, 75) + bptree_insert(root, 80) + bptree_delete(root, 47) + # bptree_delete(root, 80) + bptree_delete(root, 21) + bptree_delete(root, 22) + bptree_delete(root, 12) + bptree_delete(root, 8) bptree_print(root)