📜  数据结构 | B 和 B+ 树 |问题 3(1)

📅  最后修改于: 2023-12-03 15:10:19.509000             🧑  作者: Mango

数据结构 | B 和 B+ 树 |问题 3

介绍

当我们需要在海量数据中进行高效的查找和排序时,B树和B+树是一种很好的选择。这两种树结构都是一种平衡树,可以在平均时间复杂度为 O(log n) 的情况下进行搜索、插入和删除。

B树是一种自适应数据结构,它可以适应不同大小的页(如硬盘、内存),并且可以在日志文件、数据库索引和操作系统文件系统等场景中广泛应用。B+树是在B树的基础上进行改进,它更适合用于高效地执行范围查询操作。

问题 3
  • 问题:如何将一个元素从B树或B+树中删除?

从B树或B+树中删除一个元素需要执行以下步骤:

  1. 首先,在B树或B+树中搜索要删除的元素,找到它所在的叶节点。
  2. 删除这个叶节点中的元素,并将 B 树或B+树中对应的索引删除。
  3. 如果此时叶节点的元素数量小于树的最小度数,则必须进行一些重构操作来恢复平衡。如果根节点的元素数量为0,则需要将其删除,并且将树的高度降低一级。
  4. 如果删除的元素是一个内部节点,就需要在这个节点的左子树或右子树中找到下一个比它大的元素(或者下一个比它小的元素),替换掉要删除的元素。这个操作需要递归执行,以确保树的结构保持平衡。

以下是B树删除一个元素的Python代码示例:

class BNode:
    def __init__(self, leaf=False):
        self.keys = []
        self.child = []
        self.leaf = leaf

    def find(self, k):
        i = 0
        while i < len(self.keys) and k > self.keys[i][0]:
            i += 1
        if i < len(self.keys) and k == self.keys[i][0]:
            return self.keys[i][1]
        elif self.leaf:
            return None
        else:
            return self.child[i].find(k)

    def insert(self, k, v):
        i = 0
        while i < len(self.keys) and k > self.keys[i][0]:
            i += 1
        if i < len(self.keys) and k == self.keys[i][0]:
            self.keys[i] = (k, v)
        elif self.leaf:
            self.keys.insert(i, (k, v))
        else:
            if len(self.child[i].keys) == 2 * t - 1:
                self.split_child(i)
                if k > self.keys[i][0]:
                    i += 1
            self.child[i].insert(k, v)

    def split_child(self, i):
        t = BNode.t
        x = self.child[i]
        y = BNode(leaf=x.leaf)
        self.child.insert(i + 1, y)
        self.keys.insert(i, x.keys[t - 1])
        y.keys = x.keys[t:]
        x.keys = x.keys[:t - 1]
        y.child = x.child[t:]
        x.child = x.child[:t - 1]

    def __str__(self):
        return ('Leaf ' if self.leaf else 'Internal ') + \
               ''.join(str(self.keys[i]) for i in range(len(self.keys))) + \
               '  ' + ','.join(str(self.child[i].keys[0][0]) for i in range(len(self.child)))

    @classmethod
    def set_order(cls, t):
        cls.t = t


class BTree:
    def __init__(self, t):
        BNode.set_order(t)
        self.root = BNode(leaf=True)

    def __getitem__(self, k):
        return self.root.find(k)

    def insert(self, k, v):
        if len(self.root.keys) == 2 * BNode.t - 1:
            new_root = BNode()
            new_root.child.append(self.root)
            new_root.split_child(0)
            self.root = new_root
        self.root.insert(k, v)

    def __str__(self):
        return str(self.root)

    def find_min(self, node):
        if not node:
            return None
        elif node.leaf:
            return node.keys[0][0]
        else:
            return self.find_min(node.child[0])

    def delete(self, k):
        return self._delete(self.root, k)

    def _delete(self, node, k):
        t = BNode.t
        i = 0
        while i < len(node.keys) and k > node.keys[i][0]:
            i += 1
        if node.leaf:
            if i < len(node.keys) and node.keys[i][0] == k:
                node.keys.pop(i)
                return True
            else:
                return False
        elif i < len(node.keys) and node.keys[i][0] == k:
            if len(node.child[i].keys) >= t:
                node.keys[i] = (node.child[i].keys[-1][0], node.child[i].keys[-1][1])
                self._delete(node.child[i], node.child[i].keys[-1][0])
            elif len(node.child[i + 1].keys) >= t:
                node.keys[i] = (node.child[i + 1].keys[0][0], node.child[i + 1].keys[0][1])
                self._delete(node.child[i + 1], node.child[i + 1].keys[0][0])
            else:
                node.child[i].keys.append(node.keys[i])
                node.child[i].keys += node.child[i + 1].keys
                node.child[i].child += node.child[i + 1].child
                node.child.pop(i + 1)
                node.keys.pop(i)
                if len(node.keys) == 0:
                    self.root = node.child[0]
            return True
        else:
            if len(node.child[i].keys) == t - 1:
                if i != 0 and len(node.child[i - 1].keys) >= t:
                    node.child[i].keys.insert(0, node.keys[i - 1])
                    node.child[i].child.insert(0, node.child[i - 1].child.pop())
                    node.keys[i - 1] = node.child[i - 1].keys.pop()
                elif i != len(node.keys) and len(node.child[i + 1].keys) >= t:
                    node.child[i].keys.append(node.keys[i])
                    node.child[i].child.append(node.child[i + 1].child.pop(0))
                    node.keys[i] = node.child[i + 1].keys.pop(0)
                elif i != 0:
                    node.child[i - 1].keys.append(node.keys[i - 1])
                    node.child[i - 1].keys += node.child[i].keys
                    node.child[i - 1].child += node.child[i].child
                    node.child.pop(i)
                    node.keys.pop(i - 1)
                else:
                    node.child[0].keys += node.child[1].keys
                    node.child[0].child += node.child[1].child
                    node.child.pop(1)
                    node.keys.pop(0)
                return self._delete(node.child[i], k)
            else:
                return self._delete(node.child[i], k)

以上是B树删除一个元素的Python代码示例,可以通过执行以下代码实现删除操作:

t = 3
bt = BTree(t)
for i in range(10):
    bt.insert('k' + str(i), i)
print(bt)
bt.delete('k5')
print(bt)

下面是B+树删除一个元素的Python代码示例:

class BPlusTreeNode:
    def __init__(self, leaf=False):
        # m叉树
        self.key = []
        self.child = []
        # 是否为叶子节点
        self.leaf = leaf
        self.next = None

    @classmethod
    def set_order(cls, m):
        cls.m = m

    def is_full(self):
        return len(self.key) == BPlusTreeNode.m - 1

    def split(self, parent, at_idx):
        assert self.is_full()
        new_node = type(self)()
        mid_idx = BPlusTreeNode.m // 2 - 1
        mid_key, self.key[mid_idx] = self.key[mid_idx], None

        new_node.key = self.key[mid_idx + 1:]
        self.key = self.key[:mid_idx]

        if not self.leaf:
            new_node.child = self.child[mid_idx + 1:]
            self.child = self.child[:mid_idx + 1]

        new_node.next = self.next
        self.next = new_node

        parent.insert_child(mid_key, self, new_node, at_idx)

    def insert_child(self, key, left, right, at_idx):
        self.key.insert(at_idx, key)
        self.child.insert(at_idx, left)
        self.child[at_idx + 1] = right

    def remove_child(self, at_idx):
        self.key.pop(at_idx)
        self.child.pop(at_idx + 1)


class BPlusTree:
    def __init__(self, m):
        BPlusTreeNode.set_order(m)
        self.root = BPlusTreeNode(leaf=True)

    def search(self, key):
        node = self.root
        while not node.leaf:
            idx = node.key.index(key) if key in node.key else node.find_idx(key)
            node = node.child[idx]
        assert node is None or key in node.key
        return node

    def insert(self, key):
        node = self.root
        if node.is_full():
            new_root = BPlusTreeNode()
            self.root = new_root
            node.split(new_root, 0)
            self._insert(new_root, key)
        else:
            self._insert(node, key)

    def _insert(self, node, key):
        insert_idx = node.find_idx(key)
        if node.leaf:
            node.key.insert(insert_idx, key)
        else:
            child = node.child[insert_idx]
            if child.is_full():
                node.split(node, insert_idx)
                if key > node.key[insert_idx]:
                    insert_idx += 1
                child = node.child[insert_idx]
            self._insert(child, key)

    def delete(self, key):
        node = self.search(key)
        if node:
            assert key in node.key
            delete_idx = node.key.index(key)
            node.key.pop(delete_idx)
            if node.is_empty():
                if node is self.root:
                    self.root = None
                else:
                    node.parent.remove_child(node.index)
                    if node.next and not node.next.is_minimal():
                        node.key.append(node.next.key.pop(0))
                        if not node.leaf:
                            node.child.append(node.next.child.pop(0))
                    node.join()

    def __repr__(self):
        keys = []

        def traverse(node):
            if node:
                if node.leaf:
                    keys.extend(node.key)
                else:
                    for i in range(len(node.key)):
                        traverse(node.child[i])
                        keys.append(node.key[i])
                    traverse(node.child[-1])

        traverse(self.root)
        return repr(keys)


bt = BPlusTree(4)
for i in range(20):
    bt.insert(i)
print(bt)

bt.delete(7)
print(bt)

以上是B+树删除一个元素的Python代码示例,可以通过执行以下代码实现删除操作:

bt = BPlusTree(4)
for i in range(20):
    bt.insert(i)
print(bt)

bt.delete(7)
print(bt)
结论

使用B树或B+树进行高效的查找和排序是很好的选择,但是在实现中需要注意的细节也很多,需要仔细地考虑各种情况。在实际场景中,我们需要根据数据的特点来选择合适的度数和节点大小,以确保整个树结构的效率和可靠性。