原文: https://www.programiz.com/dsa/b-tree

在本教程中,您将学习什么是 B 树。 此外,您还将在 C,C++ ,Java 和 Python 的 B 树上找到搜索操作的工作示例。

B 树是一种特殊类型的自平衡搜索树,其中每个节点可以包含多个关键字,并且可以具有两个以上子节点。 它是二叉搜索树的广义形式。

它也被称为高度平衡的 m-way 树。

B 树 - 图1

B-tree


为什么选择 B 树?

随着访问硬盘等物理存储介质所需的时间越来越少,对 B 树的需求也随之增加。 辅助存储设备速度较慢,容量较大。 需要这种类型的数据结构以最小化磁盘访问。

其他数据结构(例如二叉搜索树,avl 树,红黑树等)只能在一个节点中存储一个键。 如果必须存储大量键,则此类树的高度将变得非常大,并且访问时间会增加。

但是,B 树可以在一个节点中存储许多键,并且可以具有多个子节点。 这样可以大大降低高度,从而可以更快地访问磁盘。


B 树属性

  1. 对于每个节点x,键以升序存储。
  2. 在每个节点中,都有一个布尔值x.leaf,如果x是叶,则为true
  3. 如果n是树的顺序,则每个内部节点最多可以包含n-1键以及指向每个子节点的指针。
  4. 除根节点外,每个节点最多可以有n个子节点,并且至少n / 2个子节点。
  5. 所有叶子具有相同的深度(即树的高度h)。
  6. 根至少有 2 个子节点,并且至少包含 1 个键。
  7. 如果n≥1,则对于任何高度为h且最小度为t ≥ 2n键 B 树,h ≥ log_t(n+1)/2

工作原理

搜索

在 B 树中搜索元素是在二叉搜索树中搜索元素的一般形式。 遵循以下步骤。

  1. 从根节点开始,将k与节点的第一个键进行比较。
    如果为k = the first key of the node,则返回节点和索引。
  2. 如果为k.leaf = true,则返回NULL(即未找到)。
  3. 如果k < the first key of the root node,则递归搜索此键的左子级。
  4. 如果当前节点和k > the first key中有多个键,请将k与该节点中的下一个键进行比较。
    如果为k < next key,则搜索该键的左子键(即k位于第一键和第二键之间)。
    否则,搜索键的右子级。
  5. 重复步骤 1 到 4,直到到达叶子。

搜索示例

  1. 让我们在等级 3 下面的树中搜索关键字k = 17
    B 树 - 图2
    B 树

  2. 在根目录中找不到k,因此,请将其与根键进行比较。
    B 树 - 图3
    在根节点上找不到k

  3. k > 11开始,转到根节点的右子节点。
    B 树 - 图4
    转到右侧的子树

  4. 比较k与 16。由于k > 16,将 k 与下一个键 18 比较。
    B 树 - 图5
    与从左到右的键比较

  5. 由于k < 18,k 在 16 到 18 之间。在右子级 16 或左子级 18 中搜索。
    B 树 - 图6
    k在 16 到 18 之间

  6. 找到了k
    B 树 - 图7
    找到了k


搜索元素的算法

  1. BtreeSearch(x, k)
  2. i = 1
  3. while i n[x] and k keyi[x] // n[x] means number of keys in x node
  4. do i = i + 1
  5. if i n[x] and k = keyi[x]
  6. then return (x, i)
  7. if leaf [x]
  8. then return NIL
  9. else
  10. return BtreeSearch(ci[x], k)

要了解有关不同 B 树操作的更多信息,请访问


Python,Java 和 C/C++ 示例

  1. # Searching a key on a B-tree in Python
  2. # Create node
  3. class BTreeNode:
  4. def __init__(self, leaf=False):
  5. self.leaf = leaf
  6. self.keys = []
  7. self.child = []
  8. class BTree:
  9. def __init__(self, t):
  10. self.root = BTreeNode(True)
  11. self.t = t
  12. # Print the tree
  13. def print_tree(self, x, l=0):
  14. print("Level ", l, " ", len(x.keys), end=":")
  15. for i in x.keys:
  16. print(i, end=" ")
  17. print()
  18. l += 1
  19. if len(x.child) > 0:
  20. for i in x.child:
  21. self.print_tree(i, l)
  22. # Search key
  23. def search_key(self, k, x=None):
  24. if x is not None:
  25. i = 0
  26. while i < len(x.keys) and k > x.keys[i][0]:
  27. i += 1
  28. if i < len(x.keys) and k == x.keys[i][0]:
  29. return (x, i)
  30. elif x.leaf:
  31. return None
  32. else:
  33. return self.search_key(k, x.child[i])
  34. else:
  35. return self.search_key(k, self.root)
  36. # Insert the key
  37. def insert_key(self, k):
  38. root = self.root
  39. if len(root.keys) == (2 * self.t) - 1:
  40. temp = BTreeNode()
  41. self.root = temp
  42. temp.child.insert_key(0, root)
  43. self.split(temp, 0)
  44. self.insert_non_full(temp, k)
  45. else:
  46. self.insert_non_full(root, k)
  47. # Insert non full condition
  48. def insert_non_full(self, x, k):
  49. i = len(x.keys) - 1
  50. if x.leaf:
  51. x.keys.append((None, None))
  52. while i >= 0 and k[0] < x.keys[i][0]:
  53. x.keys[i + 1] = x.keys[i]
  54. i -= 1
  55. x.keys[i + 1] = k
  56. else:
  57. while i >= 0 and k[0] < x.keys[i][0]:
  58. i -= 1
  59. i += 1
  60. if len(x.child[i].keys) == (2 * self.t) - 1:
  61. self.split(x, i)
  62. if k[0] > x.keys[i][0]:
  63. i += 1
  64. self.insert_non_full(x.child[i], k)
  65. # Split
  66. def split(self, x, i):
  67. t = self.t
  68. y = x.child[i]
  69. z = BTreeNode(y.leaf)
  70. x.child.insert_key(i + 1, z)
  71. x.keys.insert_key(i, y.keys[t - 1])
  72. z.keys = y.keys[t: (2 * t) - 1]
  73. y.keys = y.keys[0: t - 1]
  74. if not y.leaf:
  75. z.child = y.child[t: 2 * t]
  76. y.child = y.child[0: t - 1]
  77. def main():
  78. B = BTree(3)
  79. for i in range(10):
  80. B.insert_key((i, 2 * i))
  81. B.print_tree(B.root)
  82. if B.search_key(8) is not None:
  83. print("\nFound")
  84. else:
  85. print("\nNot found")
  86. if __name__ == '__main__':
  87. main()
  1. // Searching a key on a B-tree in Java
  2. public class BTree {
  3. private int T;
  4. // Node creation
  5. public class Node {
  6. int n;
  7. int key[] = new int[2 * T - 1];
  8. Node child[] = new Node[2 * T];
  9. boolean leaf = true;
  10. public int Find(int k) {
  11. for (int i = 0; i < this.n; i++) {
  12. if (this.key[i] == k) {
  13. return i;
  14. }
  15. }
  16. return -1;
  17. };
  18. }
  19. public BTree(int t) {
  20. T = t;
  21. root = new Node();
  22. root.n = 0;
  23. root.leaf = true;
  24. }
  25. private Node root;
  26. // Search key
  27. private Node Search(Node x, int key) {
  28. int i = 0;
  29. if (x == null)
  30. return x;
  31. for (i = 0; i < x.n; i++) {
  32. if (key < x.key[i]) {
  33. break;
  34. }
  35. if (key == x.key[i]) {
  36. return x;
  37. }
  38. }
  39. if (x.leaf) {
  40. return null;
  41. } else {
  42. return Search(x.child[i], key);
  43. }
  44. }
  45. // Splitting the node
  46. private void Split(Node x, int pos, Node y) {
  47. Node z = new Node();
  48. z.leaf = y.leaf;
  49. z.n = T - 1;
  50. for (int j = 0; j < T - 1; j++) {
  51. z.key[j] = y.key[j + T];
  52. }
  53. if (!y.leaf) {
  54. for (int j = 0; j < T; j++) {
  55. z.child[j] = y.child[j + T];
  56. }
  57. }
  58. y.n = T - 1;
  59. for (int j = x.n; j >= pos + 1; j--) {
  60. x.child[j + 1] = x.child[j];
  61. }
  62. x.child[pos + 1] = z;
  63. for (int j = x.n - 1; j >= pos; j--) {
  64. x.key[j + 1] = x.key[j];
  65. }
  66. x.key[pos] = y.key[T - 1];
  67. x.n = x.n + 1;
  68. }
  69. // Inserting a value
  70. public void Insert(final int key) {
  71. Node r = root;
  72. if (r.n == 2 * T - 1) {
  73. Node s = new Node();
  74. root = s;
  75. s.leaf = false;
  76. s.n = 0;
  77. s.child[0] = r;
  78. Split(s, 0, r);
  79. insertValue(s, key);
  80. } else {
  81. insertValue(r, key);
  82. }
  83. }
  84. // Insert the node
  85. final private void insertValue(Node x, int k) {
  86. if (x.leaf) {
  87. int i = 0;
  88. for (i = x.n - 1; i >= 0 && k < x.key[i]; i--) {
  89. x.key[i + 1] = x.key[i];
  90. }
  91. x.key[i + 1] = k;
  92. x.n = x.n + 1;
  93. } else {
  94. int i = 0;
  95. for (i = x.n - 1; i >= 0 && k < x.key[i]; i--) {
  96. }
  97. ;
  98. i++;
  99. Node tmp = x.child[i];
  100. if (tmp.n == 2 * T - 1) {
  101. Split(x, i, tmp);
  102. if (k > x.key[i]) {
  103. i++;
  104. }
  105. }
  106. insertValue(x.child[i], k);
  107. }
  108. }
  109. public void Show() {
  110. Show(root);
  111. }
  112. // Display
  113. private void Show(Node x) {
  114. assert (x == null);
  115. for (int i = 0; i < x.n; i++) {
  116. System.out.print(x.key[i] + " ");
  117. }
  118. if (!x.leaf) {
  119. for (int i = 0; i < x.n + 1; i++) {
  120. Show(x.child[i]);
  121. }
  122. }
  123. }
  124. // Check if present
  125. public boolean Contain(int k) {
  126. if (this.Search(root, k) != null) {
  127. return true;
  128. } else {
  129. return false;
  130. }
  131. }
  132. public static void main(String[] args) {
  133. BTree b = new BTree(3);
  134. b.Insert(8);
  135. b.Insert(9);
  136. b.Insert(10);
  137. b.Insert(11);
  138. b.Insert(15);
  139. b.Insert(20);
  140. b.Insert(17);
  141. b.Show();
  142. if (b.Contain(12)) {
  143. System.out.println("\nfound");
  144. } else {
  145. System.out.println("\nnot found");
  146. }
  147. ;
  148. }
  149. }
  1. // Searching a key on a B-tree in C
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #define MAX 3
  5. #define MIN 2
  6. struct BTreeNode {
  7. int val[MAX + 1], count;
  8. struct BTreeNode *link[MAX + 1];
  9. };
  10. struct BTreeNode *root;
  11. // Create a node
  12. struct BTreeNode *createNode(int val, struct BTreeNode *child) {
  13. struct BTreeNode *newNode;
  14. newNode = (struct BTreeNode *)malloc(sizeof(struct BTreeNode));
  15. newNode->val[1] = val;
  16. newNode->count = 1;
  17. newNode->link[0] = root;
  18. newNode->link[1] = child;
  19. return newNode;
  20. }
  21. // Insert node
  22. void insertNode(int val, int pos, struct BTreeNode *node,
  23. struct BTreeNode *child) {
  24. int j = node->count;
  25. while (j > pos) {
  26. node->val[j + 1] = node->val[j];
  27. node->link[j + 1] = node->link[j];
  28. j--;
  29. }
  30. node->val[j + 1] = val;
  31. node->link[j + 1] = child;
  32. node->count++;
  33. }
  34. // Split node
  35. void splitNode(int val, int *pval, int pos, struct BTreeNode *node,
  36. struct BTreeNode *child, struct BTreeNode **newNode) {
  37. int median, j;
  38. if (pos > MIN)
  39. median = MIN + 1;
  40. else
  41. median = MIN;
  42. *newNode = (struct BTreeNode *)malloc(sizeof(struct BTreeNode));
  43. j = median + 1;
  44. while (j <= MAX) {
  45. (*newNode)->val[j - median] = node->val[j];
  46. (*newNode)->link[j - median] = node->link[j];
  47. j++;
  48. }
  49. node->count = median;
  50. (*newNode)->count = MAX - median;
  51. if (pos <= MIN) {
  52. insertNode(val, pos, node, child);
  53. } else {
  54. insertNode(val, pos - median, *newNode, child);
  55. }
  56. *pval = node->val[node->count];
  57. (*newNode)->link[0] = node->link[node->count];
  58. node->count--;
  59. }
  60. // Set the value
  61. int setValue(int val, int *pval,
  62. struct BTreeNode *node, struct BTreeNode **child) {
  63. int pos;
  64. if (!node) {
  65. *pval = val;
  66. *child = NULL;
  67. return 1;
  68. }
  69. if (val < node->val[1]) {
  70. pos = 0;
  71. } else {
  72. for (pos = node->count;
  73. (val < node->val[pos] && pos > 1); pos--)
  74. ;
  75. if (val == node->val[pos]) {
  76. printf("Duplicates are not permitted\n");
  77. return 0;
  78. }
  79. }
  80. if (setValue(val, pval, node->link[pos], child)) {
  81. if (node->count < MAX) {
  82. insertNode(*pval, pos, node, *child);
  83. } else {
  84. splitNode(*pval, pval, pos, node, *child, child);
  85. return 1;
  86. }
  87. }
  88. return 0;
  89. }
  90. // Insert the value
  91. void insert(int val) {
  92. int flag, i;
  93. struct BTreeNode *child;
  94. flag = setValue(val, &i, root, &child);
  95. if (flag)
  96. root = createNode(i, child);
  97. }
  98. // Search node
  99. void search(int val, int *pos, struct BTreeNode *myNode) {
  100. if (!myNode) {
  101. return;
  102. }
  103. if (val < myNode->val[1]) {
  104. *pos = 0;
  105. } else {
  106. for (*pos = myNode->count;
  107. (val < myNode->val[*pos] && *pos > 1); (*pos)--)
  108. ;
  109. if (val == myNode->val[*pos]) {
  110. printf("%d is found", val);
  111. return;
  112. }
  113. }
  114. search(val, pos, myNode->link[*pos]);
  115. return;
  116. }
  117. // Traverse then nodes
  118. void traversal(struct BTreeNode *myNode) {
  119. int i;
  120. if (myNode) {
  121. for (i = 0; i < myNode->count; i++) {
  122. traversal(myNode->link[i]);
  123. printf("%d ", myNode->val[i + 1]);
  124. }
  125. traversal(myNode->link[i]);
  126. }
  127. }
  128. int main() {
  129. int val, ch;
  130. insert(8);
  131. insert(9);
  132. insert(10);
  133. insert(11);
  134. insert(15);
  135. insert(16);
  136. insert(17);
  137. insert(18);
  138. insert(20);
  139. insert(23);
  140. traversal(root);
  141. printf("\n");
  142. search(11, &ch, root);
  143. }
// Searching a key on a B-tree in C++

#include <iostream>
using namespace std;

class TreeNode {
  int *keys;
  int t;
  TreeNode **C;
  int n;
  bool leaf;

   public:
  TreeNode(int temp, bool bool_leaf);

  void insertNonFull(int k);
  void splitChild(int i, TreeNode *y);
  void traverse();

  TreeNode *search(int k);

  friend class BTree;
};

class BTree {
  TreeNode *root;
  int t;

   public:
  BTree(int temp) {
    root = NULL;
    t = temp;
  }

  void traverse() {
    if (root != NULL)
      root->traverse();
  }

  TreeNode *search(int k) {
    return (root == NULL) ? NULL : root->search(k);
  }

  void insert(int k);
};

TreeNode::TreeNode(int t1, bool leaf1) {
  t = t1;
  leaf = leaf1;

  keys = new int[2 * t - 1];
  C = new TreeNode *[2 * t];

  n = 0;
}

void TreeNode::traverse() {
  int i;
  for (i = 0; i < n; i++) {
    if (leaf == false)
      C[i]->traverse();
    cout << " " << keys[i];
  }

  if (leaf == false)
    C[i]->traverse();
}

TreeNode *TreeNode::search(int k) {
  int i = 0;
  while (i < n && k > keys[i])
    i++;

  if (keys[i] == k)
    return this;

  if (leaf == true)
    return NULL;

  return C[i]->search(k);
}

void BTree::insert(int k) {
  if (root == NULL) {
    root = new TreeNode(t, true);
    root->keys[0] = k;
    root->n = 1;
  } else {
    if (root->n == 2 * t - 1) {
      TreeNode *s = new TreeNode(t, false);

      s->C[0] = root;

      s->splitChild(0, root);

      int i = 0;
      if (s->keys[0] < k)
        i++;
      s->C[i]->insertNonFull(k);

      root = s;
    } else
      root->insertNonFull(k);
  }
}

void TreeNode::insertNonFull(int k) {
  int i = n - 1;

  if (leaf == true) {
    while (i >= 0 && keys[i] > k) {
      keys[i + 1] = keys[i];
      i--;
    }

    keys[i + 1] = k;
    n = n + 1;
  } else {
    while (i >= 0 && keys[i] > k)
      i--;

    if (C[i + 1]->n == 2 * t - 1) {
      splitChild(i + 1, C[i + 1]);

      if (keys[i + 1] < k)
        i++;
    }
    C[i + 1]->insertNonFull(k);
  }
}

void TreeNode::splitChild(int i, TreeNode *y) {
  TreeNode *z = new TreeNode(y->t, y->leaf);
  z->n = t - 1;

  for (int j = 0; j < t - 1; j++)
    z->keys[j] = y->keys[j + t];

  if (y->leaf == false) {
    for (int j = 0; j < t; j++)
      z->C[j] = y->C[j + t];
  }

  y->n = t - 1;
  for (int j = n; j >= i + 1; j--)
    C[j + 1] = C[j];

  C[i + 1] = z;

  for (int j = n - 1; j >= i; j--)
    keys[j + 1] = keys[j];

  keys[i] = y->keys[t - 1];
  n = n + 1;
}

int main() {
  BTree t(3);
  t.insert(8);
  t.insert(9);
  t.insert(10);
  t.insert(11);
  t.insert(15);
  t.insert(16);
  t.insert(17);
  t.insert(18);
  t.insert(20);
  t.insert(23);

  cout << "The B-tree is: ";
  t.traverse();

  int k = 10;
  (t.search(k) != NULL) ? cout << endl
                 << k << " is found"
              : cout << endl
                 << k << " is not Found";

  k = 2;
  (t.search(k) != NULL) ? cout << endl
                 << k << " is found"
              : cout << endl
                 << k << " is not Found\n";
}

B 树上的搜索的复杂度

最坏情况下的时间复杂度:Θ(log n)

平均情况时间复杂度:Θ(log n)

最佳情况时间复杂度:Θ(log n)

平均情况空间复杂度:Θ(n)

最坏情况的空间复杂度:Θ(n)


B 树应用

  • 数据库和文件系统
  • 存储数据块(辅助存储介质)
  • 多级索引