了解二叉搜索树

    在本教程中,您将学习二进制搜索树的工作原理。此外,您还将找到C中二叉搜索树的示例。
    二叉搜索树是一种数据结构,它可以让我们快速地维护一个排序的数字列表。

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

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

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

在这里插入图片描述
    右边的二叉树不是二叉搜索树,因为节点“3”的右边子树包含一个小于它的值。
    有两种基本操作可以在二叉搜索树上执行:搜索和插入。

搜索操作

    该算法依赖于二叉搜索树的性质,即如果每个左子树的值低于根,而每个右子树的值高于根。
    如果值比根值小,我们可以确定值不在右子树中,我们只需要在左子树中搜索;如果值比根值大,我们可以确定值不在左子树中,我们只需要在右子树中搜索。

算法
If root == NULL 
    return NULL;
If number == root->data 
    return root->data;
If number < root->data 
    return search(root->left)
If number > root->data 
    return search(root->right)

    让我们尝试用图表将其可视化。
在这里插入图片描述
    找不到4,因此遍历8的左子树

在这里插入图片描述
    依然找不到4,遍历3的右子树

在这里插入图片描述
    找不到4,因此遍历6的左子树
在这里插入图片描述
    找到4
    如果找到该值,我们将返回该值,以便它在每个递归步骤中传播,如下图所示。
    您可能已经注意到,我们已经调用了 return search(struct node*) 四次。当我们得到新节点或NULL值时,值会一次一次返回,直到search(root)返回最终结果。
在这里插入图片描述
    如果在任何子树中找到该值,则向上传递该值,以便最终返回该值,否则返回null。
    如果没有找到该值,我们最终会到达一个叶节点的左或右子节点,该节点为NULL,它会被传递并返回。

插入操作

    在正确的位置插入一个值类似于搜索,因为我们试图保持左子树小于根值,右子树大于根值的规则。
    根据值的不同,我们转到右子树或左子树,当到达左子树或右子树为空的点时,我们将新节点放在那里。

算法
If node == NULL 
    return createNode(data)
if (data < node->data)
    node->left  = insert(node->left, data);
else if (data > node->data)
    node->right = insert(node->right, data);  
return node;

    算法并不像看上去那么简单。让我们试着想象一下如何将一个数字添加到现有的二叉搜索树中。
在这里插入图片描述
    4<8,因此需要将4放到8的左子树。
在这里插入图片描述
    4>3,因此将4放到3的右子树
在这里插入图片描述
    4<6,因此将4放到6的左子树
在这里插入图片描述
    将4插入到6的左子树
    我们已经附加了节点,但是我们仍然必须退出函数,同时不对树的其余部分造成任何损坏。因此 return node; 在末尾派上了用场。在node等于NULL的情况下,返回新创建的节点并将其附加到父节点。否则,在返回到根节点之前,将返回未经更改的节点。
    这确保了当我们向上移动树时,其他节点连接不会改变。
在这里插入图片描述
    该图显示了最后返回根元素的重要性,这样根元素才能在向上递归步骤中不丢失其位置。

删除操作

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

第一种

    在第一种情况下,要删除的节点是叶节点。在这种情况下,只需从树中删除节点。
在这里插入图片描述
    4将被删除
在这里插入图片描述
    删除节点

第二种

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

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

在这里插入图片描述
    6将被删除
在这里插入图片描述
    将其子节点的值复制到节点并删除子节点
在这里插入图片描述
    最终树

第三种

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

  1. 获取该节点的顺序继承节点。
  2. 用顺序继承节点替换节点。
  3. 移除原始位置的顺序继承节点。

在这里插入图片描述
    3将被删除
在这里插入图片描述
    将顺序继承节点(4)的值复制到节点
在这里插入图片描述
    删除顺序继承节点

C示例
// Binary Search Tree operations in C

#include <stdio.h>
#include <stdlib.h>

struct node {
    
    
  int key;
  struct node *left, *right;
};

// Create a node
struct node *newNode(int item) {
    
    
  struct node *temp = (struct node *)malloc(sizeof(struct node));
  temp->key = item;
  temp->left = temp->right = NULL;
  return temp;
}

// Inorder Traversal
void inorder(struct node *root) {
    
    
  if (root != NULL) {
    
    
    // Traverse left
    inorder(root->left);

    // Traverse root
    printf("%d -> ", root->key);

    // Traverse right
    inorder(root->right);
  }
}

// Insert a node
struct node *insert(struct node *node, int key) {
    
    
  // Return a new node if the tree is empty
  if (node == NULL) return newNode(key);

  // Traverse to the right place and insert the node
  if (key < node->key)
    node->left = insert(node->left, key);
  else
    node->right = insert(node->right, key);

  return node;
}

// Find the inorder successor
struct node *minValueNode(struct node *node) {
    
    
  struct node *current = node;

  // Find the leftmost leaf
  while (current && current->left != NULL)
    current = current->left;

  return current;
}

// Deleting a node
struct node *deleteNode(struct node *root, int key) {
    
    
  // Return if the tree is empty
  if (root == NULL) return root;

  // Find the node to be deleted
  if (key < root->key)
    root->left = deleteNode(root->left, key);
  else if (key > root->key)
    root->right = deleteNode(root->right, key);

  else {
    
    
    // If the node is with only one child or no child
    if (root->left == NULL) {
    
    
      struct node *temp = root->right;
      free(root);
      return temp;
    } else if (root->right == NULL) {
    
    
      struct node *temp = root->left;
      free(root);
      return temp;
    }

    // If the node has two children
    struct node *temp = minValueNode(root->right);

    // Place the inorder successor in position of the node to be deleted
    root->key = temp->key;

    // Delete the inorder successor
    root->right = deleteNode(root->right, temp->key);
  }
  return root;
}

// Driver code
int main() {
    
    
  struct node *root = NULL;
  root = insert(root, 8);
  root = insert(root, 3);
  root = insert(root, 1);
  root = insert(root, 6);
  root = insert(root, 7);
  root = insert(root, 10);
  root = insert(root, 14);
  root = insert(root, 4);

  printf("Inorder traversal: ");
  inorder(root);

  printf("\nAfter deleting 10\n");
  root = deleteNode(root, 10);
  printf("Inorder traversal: ");
  inorder(root);
}
二叉搜索树的复杂度
时间复杂度
操作 最佳复杂度 平均复杂度 最差复杂度
查找 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内核中的虚拟内存区域。
参考文档

[1]Parewa Labs Pvt. Ltd.Balanced Binary Tree[EB/OL].https://www.programiz.com/dsa/binary-search-tree,2020-01-01.

猜你喜欢

转载自blog.csdn.net/zsx0728/article/details/114268058