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

在本教程中,您将学习二叉搜索树的工作原理。 此外,您还将找到 C,C++ ,Java 和 Python 中的二叉搜索树的工作示例。

二叉搜索树是一种数据结构,可快速使我们维护一个排序的数字列表。

  • 之所以称为二叉树,是因为每个树节点最多有两个子节点。
  • 之所以称为搜索树,是因为它可用于在O(log(n))时间中搜索数字的存在。

将二叉搜索树与常规二叉树分开的属性是

  1. 左子树的所有节点均小于根节点
  2. 右子树的所有节点均大于根节点
  3. 每个节点的两个子树也是 BST,即它们具有上述两个属性

二叉搜索树(BST) - 图1

显示了一个树,该树的右子树的值小于根,表明该树不是有效的二叉搜索树

右边的二叉树不是二叉搜索树,因为节点“3”的右子树包含的值小于它。

您可以对二叉搜索树执行两个基本操作:


搜索操作

该算法取决于 BST 的属性,即每个左子树的值都低于根,而每个右子树的值都高于根。

如果该值低于根,则可以确定该值不在正确的子树中。 我们只需要在左子树中搜索,如果该值在根之上,则可以肯定地说该值不在左子树中; 我们只需要在正确的子树中搜索。

算法

  1. If root == NULL
  2. return NULL;
  3. If number == root->data
  4. return root->data;
  5. If number < root->data
  6. return search(root->left)
  7. If number > root->data
  8. return search(root->right)

让我们尝试通过图表将其可视化。

二叉搜索树(BST) - 图2

找不到 4,因此遍历 8 的左子树

二叉搜索树(BST) - 图3

找不到 4,因此遍历 3 的右子树

二叉搜索树(BST) - 图4

找不到 4,因此遍历 6 的左子树

二叉搜索树(BST) - 图5

找到了 4

如果找到该值,则返回该值,以便在每个递归步骤中将其传播,如下图所示。

如果您可能已经注意到,我们已经四次调用return search(struct node *)。 当我们返回新节点或NULL时,该值会一次又一次返回,直到search(root)返回最终结果。

二叉搜索树(BST) - 图6

如果在任何子树中找到该值,则将其向上传播,以便最后返回,否则返回null

如果找不到该值,则最终到达叶节点的左或右子节点,该子节点为NULL,并传播并返回。


插入操作

在正确的位置插入值类似于搜索,因为我们尝试维护以下规则:左子树小于根,右子树大于根。

我们继续根据值去右子树或左子树,当我们到达左或右子树为null的点时,将新节点放在那里。

算法:

  1. If node == NULL
  2. return createNode(data)
  3. if (data < node->data)
  4. node->left = insert(node->left, data);
  5. else if (data > node->data)
  6. node->right = insert(node->right, data);
  7. return node;

该算法并不像看起来那样简单。 让我们尝试可视化如何将数字添加到现有的 BST。

二叉搜索树(BST) - 图7

因为4 < 8,遍历 8 的左子树

二叉搜索树(BST) - 图8

因为4 > 3,遍历 3 的右子树

二叉搜索树(BST) - 图9

因为4 < 6,遍历 6 的左子树

二叉搜索树(BST) - 图10

插入 4 作为 6 的左子级

我们已经附加了节点,但是我们仍然必须退出该函数,而不会损坏树的其余部分。 这是最后的return node;派上用场的地方。 在NULL的情况下,将返回新创建的节点并将其附加到父节点,否则在返回到根节点之前,将返回相同的节点,而不会进行任何更改。

这可以确保当我们向后移动树时,其他节点连接不会更改。

二叉搜索树(BST) - 图11

该图显示了在最后返回根元素的重要性,这样根元素才能在向上递归步骤中不丢失其位置。


删除操作

从二叉搜索树中删除节点的情况有三种。

情况一

在第一种情况下,要删除的节点是叶节点。 在这种情况下,只需从树中删除该节点即可。

二叉搜索树(BST) - 图12

4 将被删除

二叉搜索树(BST) - 图13

删除节点

情况二

在第二种情况下,要删除的节点只有一个子节点。 在这种情况下,请执行以下步骤:

  1. 将该节点替换为其子节点。
  2. 从其原始位置删除子节点。

二叉搜索树(BST) - 图14

6 将被删除

二叉搜索树(BST) - 图15

将其子项的值复制到节点并删除该子项

二叉搜索树(BST) - 图16

最终的树

情况三

在第三种情况下,要删除的节点有两个子级。 在这种情况下,请执行以下步骤:

  1. 获取该节点的有序后继。
  2. 将节点替换为有序后继节点。
  3. 从原始位置卸下有序后继。

二叉搜索树(BST) - 图17

3 将被删除

二叉搜索树(BST) - 图18

将有序后继(4)的值复制到节点

二叉搜索树(BST) - 图19

删除顺序后继


Python,Java 和 C/C++ 示例

  1. # Binary Search Tree operations in Python
  2. # Create a node
  3. class Node:
  4. def __init__(self, key):
  5. self.key = key
  6. self.left = None
  7. self.right = None
  8. # Inorder traversal
  9. def inorder(root):
  10. if root is not None:
  11. # Traverse left
  12. inorder(root.left)
  13. # Traverse root
  14. print(str(root.key) + "->", end=' ')
  15. # Traverse right
  16. inorder(root.right)
  17. # Insert a node
  18. def insert(node, key):
  19. # Return a new node if the tree is empty
  20. if node is None:
  21. return Node(key)
  22. # Traverse to the right place and insert the node
  23. if key < node.key:
  24. node.left = insert(node.left, key)
  25. else:
  26. node.right = insert(node.right, key)
  27. return node
  28. # Find the inorder successor
  29. def minValueNode(node):
  30. current = node
  31. # Find the leftmost leaf
  32. while(current.left is not None):
  33. current = current.left
  34. return current
  35. # Deleting a node
  36. def deleteNode(root, key):
  37. # Return if the tree is empty
  38. if root is None:
  39. return root
  40. # Find the node to be deleted
  41. if key < root.key:
  42. root.left = deleteNode(root.left, key)
  43. elif(key > root.key):
  44. root.right = deleteNode(root.right, key)
  45. else:
  46. # If the node is with only one child or no child
  47. if root.left is None:
  48. temp = root.right
  49. root = None
  50. return temp
  51. elif root.right is None:
  52. temp = root.left
  53. root = None
  54. return temp
  55. # If the node has two children,
  56. # place the inorder successor in position of the node to be deleted
  57. temp = minValueNode(root.right)
  58. root.key = temp.key
  59. # Delete the inorder successor
  60. root.right = deleteNode(root.right, temp.key)
  61. return root
  62. root = None
  63. root = insert(root, 8)
  64. root = insert(root, 3)
  65. root = insert(root, 1)
  66. root = insert(root, 6)
  67. root = insert(root, 7)
  68. root = insert(root, 10)
  69. root = insert(root, 14)
  70. root = insert(root, 4)
  71. print("Inorder traversal: ", end=' ')
  72. inorder(root)
  73. print("\nDelete 10")
  74. root = deleteNode(root, 10)
  75. print("Inorder traversal: ", end=' ')
  76. inorder(root)
  1. // Binary Search Tree operations in Java
  2. class BinarySearchTree {
  3. class Node {
  4. int key;
  5. Node left, right;
  6. public Node(int item) {
  7. key = item;
  8. left = right = null;
  9. }
  10. }
  11. Node root;
  12. BinarySearchTree() {
  13. root = null;
  14. }
  15. void insert(int key) {
  16. root = insertKey(root, key);
  17. }
  18. // Insert key in the tree
  19. Node insertKey(Node root, int key) {
  20. // Return a new node if the tree is empty
  21. if (root == null) {
  22. root = new Node(key);
  23. return root;
  24. }
  25. // Traverse to the right place and insert the node
  26. if (key < root.key)
  27. root.left = insertKey(root.left, key);
  28. else if (key > root.key)
  29. root.right = insertKey(root.right, key);
  30. return root;
  31. }
  32. void inorder() {
  33. inorderRec(root);
  34. }
  35. // Inorder Traversal
  36. void inorderRec(Node root) {
  37. if (root != null) {
  38. inorderRec(root.left);
  39. System.out.print(root.key + " -> ");
  40. inorderRec(root.right);
  41. }
  42. }
  43. void deleteKey(int key) {
  44. root = deleteRec(root, key);
  45. }
  46. Node deleteRec(Node root, int key) {
  47. // Return if the tree is empty
  48. if (root == null)
  49. return root;
  50. // Find the node to be deleted
  51. if (key < root.key)
  52. root.left = deleteRec(root.left, key);
  53. else if (key > root.key)
  54. root.right = deleteRec(root.right, key);
  55. else {
  56. // If the node is with only one child or no child
  57. if (root.left == null)
  58. return root.right;
  59. else if (root.right == null)
  60. return root.left;
  61. // If the node has two children
  62. // Place the inorder successor in position of the node to be deleted
  63. root.key = minValue(root.right);
  64. // Delete the inorder successor
  65. root.right = deleteRec(root.right, root.key);
  66. }
  67. return root;
  68. }
  69. // Find the inorder successor
  70. int minValue(Node root) {
  71. int minv = root.key;
  72. while (root.left != null) {
  73. minv = root.left.key;
  74. root = root.left;
  75. }
  76. return minv;
  77. }
  78. // Driver Program to test above functions
  79. public static void main(String[] args) {
  80. BinarySearchTree tree = new BinarySearchTree();
  81. tree.insert(8);
  82. tree.insert(3);
  83. tree.insert(1);
  84. tree.insert(6);
  85. tree.insert(7);
  86. tree.insert(10);
  87. tree.insert(14);
  88. tree.insert(4);
  89. System.out.print("Inorder traversal: ");
  90. tree.inorder();
  91. System.out.println("\n\nAfter deleting 10");
  92. tree.deleteKey(10);
  93. System.out.print("Inorder traversal: ");
  94. tree.inorder();
  95. }
  96. }
  1. // Binary Search Tree operations in C
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. struct node {
  5. int key;
  6. struct node *left, *right;
  7. };
  8. // Create a node
  9. struct node *newNode(int item) {
  10. struct node *temp = (struct node *)malloc(sizeof(struct node));
  11. temp->key = item;
  12. temp->left = temp->right = NULL;
  13. return temp;
  14. }
  15. // Inorder Traversal
  16. void inorder(struct node *root) {
  17. if (root != NULL) {
  18. // Traverse left
  19. inorder(root->left);
  20. // Traverse root
  21. printf("%d -> ", root->key);
  22. // Traverse right
  23. inorder(root->right);
  24. }
  25. }
  26. // Insert a node
  27. struct node *insert(struct node *node, int key) {
  28. // Return a new node if the tree is empty
  29. if (node == NULL) return newNode(key);
  30. // Traverse to the right place and insert the node
  31. if (key < node->key)
  32. node->left = insert(node->left, key);
  33. else
  34. node->right = insert(node->right, key);
  35. return node;
  36. }
  37. // Find the inorder successor
  38. struct node *minValueNode(struct node *node) {
  39. struct node *current = node;
  40. // Find the leftmost leaf
  41. while (current && current->left != NULL)
  42. current = current->left;
  43. return current;
  44. }
  45. // Deleting a node
  46. struct node *deleteNode(struct node *root, int key) {
  47. // Return if the tree is empty
  48. if (root == NULL) return root;
  49. // Find the node to be deleted
  50. if (key < root->key)
  51. root->left = deleteNode(root->left, key);
  52. else if (key > root->key)
  53. root->right = deleteNode(root->right, key);
  54. else {
  55. // If the node is with only one child or no child
  56. if (root->left == NULL) {
  57. struct node *temp = root->right;
  58. free(root);
  59. return temp;
  60. } else if (root->right == NULL) {
  61. struct node *temp = root->left;
  62. free(root);
  63. return temp;
  64. }
  65. // If the node has two children
  66. struct node *temp = minValueNode(root->right);
  67. // Place the inorder successor in position of the node to be deleted
  68. root->key = temp->key;
  69. // Delete the inorder successor
  70. root->right = deleteNode(root->right, temp->key);
  71. }
  72. return root;
  73. }
  74. // Driver code
  75. int main() {
  76. struct node *root = NULL;
  77. root = insert(root, 8);
  78. root = insert(root, 3);
  79. root = insert(root, 1);
  80. root = insert(root, 6);
  81. root = insert(root, 7);
  82. root = insert(root, 10);
  83. root = insert(root, 14);
  84. root = insert(root, 4);
  85. printf("Inorder traversal: ");
  86. inorder(root);
  87. printf("\nAfter deleting 10\n");
  88. root = deleteNode(root, 10);
  89. printf("Inorder traversal: ");
  90. inorder(root);
  91. }
  1. // Binary Search Tree operations in C++
  2. #include <iostream>
  3. using namespace std;
  4. struct node {
  5. int key;
  6. struct node *left, *right;
  7. };
  8. // Create a node
  9. struct node *newNode(int item) {
  10. struct node *temp = (struct node *)malloc(sizeof(struct node));
  11. temp->key = item;
  12. temp->left = temp->right = NULL;
  13. return temp;
  14. }
  15. // Inorder Traversal
  16. void inorder(struct node *root) {
  17. if (root != NULL) {
  18. // Traverse left
  19. inorder(root->left);
  20. // Traverse root
  21. cout << root->key << " -> ";
  22. // Traverse right
  23. inorder(root->right);
  24. }
  25. }
  26. // Insert a node
  27. struct node *insert(struct node *node, int key) {
  28. // Return a new node if the tree is empty
  29. if (node == NULL) return newNode(key);
  30. // Traverse to the right place and insert the node
  31. if (key < node->key)
  32. node->left = insert(node->left, key);
  33. else
  34. node->right = insert(node->right, key);
  35. return node;
  36. }
  37. // Find the inorder successor
  38. struct node *minValueNode(struct node *node) {
  39. struct node *current = node;
  40. // Find the leftmost leaf
  41. while (current && current->left != NULL)
  42. current = current->left;
  43. return current;
  44. }
  45. // Deleting a node
  46. struct node *deleteNode(struct node *root, int key) {
  47. // Return if the tree is empty
  48. if (root == NULL) return root;
  49. // Find the node to be deleted
  50. if (key < root->key)
  51. root->left = deleteNode(root->left, key);
  52. else if (key > root->key)
  53. root->right = deleteNode(root->right, key);
  54. else {
  55. // If the node is with only one child or no child
  56. if (root->left == NULL) {
  57. struct node *temp = root->right;
  58. free(root);
  59. return temp;
  60. } else if (root->right == NULL) {
  61. struct node *temp = root->left;
  62. free(root);
  63. return temp;
  64. }
  65. // If the node has two children
  66. struct node *temp = minValueNode(root->right);
  67. // Place the inorder successor in position of the node to be deleted
  68. root->key = temp->key;
  69. // Delete the inorder successor
  70. root->right = deleteNode(root->right, temp->key);
  71. }
  72. return root;
  73. }
  74. // Driver code
  75. int main() {
  76. struct node *root = NULL;
  77. root = insert(root, 8);
  78. root = insert(root, 3);
  79. root = insert(root, 1);
  80. root = insert(root, 6);
  81. root = insert(root, 7);
  82. root = insert(root, 10);
  83. root = insert(root, 14);
  84. root = insert(root, 4);
  85. cout << "Inorder traversal: ";
  86. inorder(root);
  87. cout << "\nAfter deleting 10\n";
  88. root = deleteNode(root, 10);
  89. cout << "Inorder traversal: ";
  90. inorder(root);
  91. }

二叉搜索树的复杂度

时间复杂度

操作 最佳情况复杂度 平均情况复杂度 最坏情况复杂度
搜索 O(log n) O(log n) O(n)
插入 O(log n) O(log n) O(n)
删除中 O(log n) O(log n) O(n)

这里,n是树中的节点数。

空间复杂度

所有操作的空间复杂度为O(n)


二叉搜索树应用

  1. 在数据库中进行多级索引
  2. 用于动态排序
  3. 用于管理 Unix 内核中的虚拟内存区域