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

Какво е бинарно дърво?

Думата двоичен означава две. В дървовидната структура на данните „Двоично дърво“ означава дърво, където всеки възел може да има максимум два дъщерни възела (ляв и десен възел). Това е просто двоично дърво.

Има обаче друго двоично дърво, което се използва най-често и има няколко случая на употреба. Нарича се Двоично дърво за търсене (BST). Това дърво може да направи алгоритъма за търсене много по-бърз, точно log(n) времева сложност. В структурата на данните n означава броя на възлите в двоичното дърво.

Какви са разликите между двоично дърво и двоично дърво за търсене?

Разликата между BST и обикновеното двоично дърво е в това, че BST левият възел има по-малка стойност от основния възел, а десният възел има по-голяма стойност от основния възел. Така че лявото поддърво винаги ще съдържа по-малка стойност от корена, а дясното поддърво винаги ще съдържа по-голяма стойност от корена.

Разликите между двоично дърво и двоично дърво за търсене

Пример за двоични дървета за търсене

Нека имаме следния пример за демонстриране на концепциите на двоичното дърво за търсене.

Пример за двоични дървета за търсене

Тук можете всички възли да следват дадената дисциплина. Има формула за максималния брой възли в двоичното дърво за търсене. Ако наблюдаваме горното дърво, можем да видим, че всеки възел има две деца, с изключение на всички листови възли. И височината (h) на даденото двоично дърво е 4. Формулата е 2h - 1. И така, дава 15.

Пример за двоични дървета за търсене

Пример за двоични дървета за търсене

Даденото по-горе изображение не е пълно двоично дърво или балансирано двоично дърво, нарича се пълно двоично дърво или балансирано двоично дърво. Има друга структура от данни, наречена AVL (друг тип двоично дърво), която оптимизира височината на двоичното дърво и извършва търсене по-бързо за BST, както е показано на фиг. 3.

Опитайте се да изчислите обхождането по ред на двоичното дърво, дадено по-горе. Ще откриете, че ще даде ненамаляващ сортиран масив, а алгоритмите за обхождане ще бъдат същите като при двоичното дърво.

Видове двоично дърво

Ето някои важни типове двоично дърво:

  • Пълно двоично дърво: Всеки възел може да има 0 или 2 дъщерни възела в това двоично дърво. Само един дъщерен възел не е разрешен в този тип двоично дърво. Така че, с изключение на листовия възел, всички възли ще имат 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

Приложение на двоично дърво

Ето някои често срещани приложения на Binary Tree:

  • Организиране на данни за възли в сортиран ред
  • Използва се в обекти на карта и набор от възли в библиотеки на езици за програмиране.
  • Търсене на елементи в структурите от данни

» Научете следващия ни урок за Алгоритъм за комбиниране