Двоичное дерево в структуре данных (ПРИМЕР)

Что такое бинарное дерево?

Слово «бинарный» означает «два». В древовидной структуре данных «двоичное дерево» означает дерево, в котором каждый узел может иметь максимум два дочерних узла (левый и правый узлы). Это простое двоичное дерево.

Однако есть еще одно двоичное дерево, которое используется чаще всего и имеет несколько вариантов использования. Это называется двоичным деревом поиска (BST). Это дерево может сделать алгоритм поиска намного быстрее, с точностью до log(n) временной сложности. В структуре данных n означает количество узлов в двоичном дереве.

Каковы различия между двоичным деревом и двоичным деревом поиска?

Разница между BST и обычным двоичным деревом заключается в том, что левый узел BST имеет меньшее значение, чем корневой узел, а правый узел имеет большее значение, чем корневой узел. Таким образом, левое поддерево всегда будет содержать меньшее значение, чем корень, а правое поддерево всегда будет содержать большее значение, чем корень.

Различия между двоичным деревом и двоичным деревом поиска

Пример двоичных деревьев поиска

Давайте рассмотрим следующий пример для демонстрации концепций двоичного дерева поиска.

Пример двоичных деревьев поиска

Здесь вы можете все узлы следовать заданной дисциплине. Существует формула максимального количества узлов в дереве двоичного поиска. Если мы посмотрим на приведенное выше дерево, мы увидим, что у каждого узла есть два дочерних узла, за исключением всех конечных узлов. А высота (h) данного двоичного дерева равна 4. Формула: 2h -1. Итак, получается 15.

Пример двоичных деревьев поиска

Пример двоичных деревьев поиска

Приведенное выше изображение не является полным двоичным деревом или сбалансированным двоичным деревом, оно называется полным двоичным деревом или сбалансированным двоичным деревом. Существует еще одна структура данных, называемая AVL (еще один тип двоичного дерева), которая оптимизирует высоту двоичного дерева и ускоряет поиск BST, как показано на рис. 3.

Попробуйте вычислить упорядоченный обход приведенного выше двоичного дерева. Вы обнаружите, что это даст неубывающий отсортированный массив, а алгоритмы обхода будут такими же, как и у двоичного дерева.

Типы двоичного дерева

Вот некоторые важные типы двоичного дерева:

  • Полное бинарное дерево: Каждый узел может иметь 0 или 2 дочерних узла в этом двоичном дереве. В этом типе двоичного дерева не допускается наличие только одного дочернего узла. Таким образом, за исключением листового узла, все узлы будут иметь двух дочерних узлов.

Типы двоичного дерева

  • Полное бинарное дерево: Каждый узел может иметь 0 или 2 узла. Это похоже на полное двоичное дерево, но все листовые элементы наклонены к левому поддереву, тогда как в полном двоичном дереве узел может находиться как в правом, так и в левом поддереве.

Типы двоичного дерева

  • Идеальное бинарное дерево: Все узлы должны иметь 0 или 2 узла, а все конечные узлы должны находиться на одном уровне или высоте. Приведенный выше пример полной двоичной древовидной структуры не является идеальным двоичным деревом, поскольку узлы 6 и узлы 1,2,3 не имеют одинаковой высоты. Но пример полного двоичного дерева — идеальное двоичное дерево.
  • Вырожденное двоичное дерево: Каждый узел может иметь только одного дочернего узла. Все операции, такие как поиск, вставка и удаление, занимают время O(N).

Типы двоичного дерева

  • Сбалансированное бинарное дерево: Вот это бинарное дерево, разница высот левого и правого поддерева не превышает 1. Итак, при добавлении или удалении узла нам нужно снова сбалансировать высоту дерева. Этот тип самобалансированного двоичного дерева называется AVL дерево.

Типы двоичного дерева

Существует три основных операции BST. Они подробно обсуждаются ниже.

Реализация двоичного дерева в C и C++

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
struct Node
{
   int value;
   struct Node *left, *right;
}
struct Node *getEmptynode(int val)
{
   struct Node *tempNode = (struct Node *)malloc(sizeof(struct Node));
   tempNode->value = val;
   tempNode->left = NULL;
   tempNode->right = NULL;
   return tempNode;
}
struct Node *successor(struct Node *node)
{
    struct Node *present = node;
// going to the left most node
    while (present != NULL && present->left != NULL)
    {
       present = present->left;
    }
     return present;
 }
struct Node *insert(struct Node *node, int value)
{
   if (node == NULL)
   {
      return getEmptynode(value);
   }
   if (value < node->value)
   {
      node->left = insert(node->left, value);
   }
   else
   {
      node->right = insert(node->right, value);
   }
      return node;
}
int searchInBST(struct Node *node, int value)
{
   struct Node *current = node;
   while (current->value != value)
    {
    if (current->value > value)
      {
      current = current->left;
      }
    else
     {
     current = current->right;
     }
   if (current == NULL)
     {
    return 0;
     }
   }
return 1;
}
void inorder(struct Node *root)
{
 if (root != NULL)
  {
   inorder(root->left);
   cout << root->value << " ";
   inorder(root->right);
  }
}
struct Node *deleteNode(struct Node *node, int value)
{
 if (node == NULL)
  {
   return node;
  }
 if (value < node->value)
  {
   node->left = deleteNode(node->left, value);
  }
else if (value > node->value)
 {
   node->right = deleteNode(node->right, value);
 }
else
{
if (node->left == NULL)
 {
 struct Node *temp = node->right;
 free(node);
 return temp;
 }
 else if (node->right == NULL)
 {
 struct Node *temp = node->left;
 free(node);
 return temp;
  }
 struct Node *temp = successor(node->right);
 node->value = temp->value;
 node->right = deleteNode(node->right, temp->value);
}
return node;
}
int main()
 {
  struct Node *root = NULL;
  root = insert(root, 8);
  root = insert(root, 4);
  root = insert(root, 12);
  root = insert(root, 2);
  root = insert(root, 6);
  root = insert(root, 10);
  root = insert(root, 14);
  root = insert(root, 1);
  root = insert(root, 3);
  root = insert(root, 5);
  root = insert(root, 7);
  root = insert(root, 9);
  root = insert(root, 11);
  root = insert(root, 13);
  root = insert(root, 15);

 cout << "InOrder Traversal after inserting all nodes: " << endl;
 inorder(root);
 root = insert(root, -10);
 cout << "\nInOrder Traversal after inserting -10 : " << endl;
 inorder(root);
 cout << "\nSearching -5 in the BST: " << searchInBST(root, -5) << endl;
 cout << "Searching -10 in the BST: " << searchInBST(root, -10) << endl;
 root = deleteNode(root,8);
 cout<<"After deleting node 8, inorder traversal: "<<endl;
 inorder(root);
 root = deleteNode(root,-10);
 cout<<"\nAfter deleting node -10, inorder traversal: "<<endl;
 inorder(root);
}

Вывод:

InOrder Traversal after inserting all nodes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

InOrder Traversal after inserting -10 :
10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Searching -5 in the BST: 0
Searching -10 in the BST: 1

After deleting node 8, inorder traversal:
-10 1 2 3 4 5 6 7 9 10 11 12 13 14 15

After deleting node -10, inorder traversal:
1 2 3 4 5 6 7 9 10 11 12 13 14 15

Реализация двоичного дерева в Python

class Node:
def __init__(self,value):
      self.left = None
      self.right = None
      self.value = value
def insert(root,value):
      if root == None:
          return Node(value)
      if value< root.value:
          root.left = insert(root.left,value)
      else:
          root.right = insert(root.right,value)
          return root
  def searchInBST(root,value):
      current = root
  while current.value != value:
  if current.value > value:
      current = current.left
  else:
      current = current.right
  if current == None:
      return "Not found"
      return "Found"
def inorder(root):
    if root != None:
      inorder(root.left)
      print(root.value,end=" ")
      inorder(root.right)
def successor(root):
    present = root
    while present != None and present.left != None:
    present = present.left
      return present
def deleteNode(root,value):
    if root == None:
      return root
    if value < root.value:
        root.left = deleteNode(root.left, value)
    elif value>root.value:
        root.right = deleteNode(root.right, value)
    else:
    if root.left == None:
        temp = root.right
        root = None
        return temp
    elif root.right == None:
        temp = root.left
        root = None
        return temp
        temp = successor(root.right)
        root.value = temp.value
        root.right = deleteNode(root.right, temp.value)
        return root
        root = Node(8)
        root = insert(root, 4)
        root = insert(root, 12)
        root = insert(root, 2)
        root = insert(root, 6)
        root = insert(root, 10)
        root = insert(root, 14)
        root = insert(root, 1)
        root = insert(root, 3)
        root = insert(root, 5)
        root = insert(root, 7)
        root = insert(root, 9)
        root = insert(root, 11)
        root = insert(root, 13)
        root = insert(root, 15)
  print("InOrder Traversal after inserting all nodes: ")
  inorder(root)
  root = insert(root, -10)
  print("\nInOrder Traversal after inserting -10 : ")
  inorder(root)
  print("\nSearching -5 in the BST: ",searchInBST(root, -5))
  print("Searching -5 in the BST: ",searchInBST(root, -10))
  root = deleteNode(root,8)
  print("After deleting node 8, inorder traversal:")
  inorder(root)
  root = deleteNode(root,-10)
  print("\nAfter deleting node -10, inorder traversal:")
  inorder(root)

Вывод:

InOrder Traversal after inserting all nodes 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 

InOrder Traversal after inserting -10 : -10 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 

Searching -5 in the BST: Not found 

Searching -5 in the BST: Found After deleting node 8, inorder traversal: -10 1 2 3 4 5 6 7 9 10 11 12 13 14 15 

After deleting node -10, inorder traversal: 1 2 3 4 5 6 7 9 10 11 12 13 14 15

Применение двоичного дерева

Вот некоторые распространенные применения двоичного дерева:

  • Организация данных узла в отсортированном порядке
  • Используется в объектах узлов карты и набора в библиотеках языков программирования.
  • Поиск элементов в структурах данных

» Узнайте наш следующий урок о Комбинированный алгоритм