الشجرة الثنائية في بنية البيانات (مثال)

⚡ ملخص ذكي

الشجرة الثنائية في هياكل البيانات هي بنية هرمية حيث تحتوي كل عقدة على طفلين على الأكثر، يُطلق عليهما اليسار واليمين. يشرح هذا المورد الأشجار الثنائية، وشجرة البحث الثنائية، وأنواعها، وتطبيقاتها، وشرحًا كاملاً بلغة C. C++و Python التنفيذ.

  • 🌳 فريف: الشجرة الثنائية هي بنية يكون لكل عقدة فيها عقدتان فرعيتان على الأكثر، اليسرى واليمنى.
  • 🔍 شجرة البحث الثنائية: في شجرة البحث الثنائية، يحمل الأطفال على اليسار قيمًا أصغر ويحمل الأطفال على اليمين قيمًا أكبر، مما يتيح البحث O(log n).
  • 🧩 الأنواع: تفرض الأشجار الثنائية الكاملة، والمثالية، والمتدهورة، والمتوازنة قواعد شكل مختلفة.
  • ⚙️ BST Operaنشوئها: تتبع عمليات الإضافة والبحث والحذف خاصية الترتيب للحفاظ على سرعة عمليات البحث.
  • ؟؟؟؟ Code قدمت: العمل C، C++و Python تُظهر البرامج عمليات الإدراج والبحث والحذف والتنقل بالترتيب الداخلي.

ما هي الشجرة الثنائية؟

كلمة ثنائي تعني اثنين. في بنية بيانات الشجرة، تعني "الشجرة الثنائية" شجرة حيث يمكن أن تحتوي كل عقدة على عقدتين فرعيتين كحد أقصى (العقد اليسرى واليمنى). إنها شجرة ثنائية بسيطة.

ومع ذلك، هناك شجرة ثنائية أخرى تُستخدم بشكل متكرر ولها عدة حالات استخدام. تُسمى شجرة البحث الثنائية (BST). يمكن لهذه الشجرة أن تجعل خوارزمية البحث أسرع بكثير، بدقة عالية مع تعقيد زمني قدره log(n). في بنية البيانات، تعني n عدد العقد في الشجرة الثنائية.

ما هي الاختلافات بين الشجرة الثنائية وشجرة البحث الثنائية؟

الفرق بين BST والشجرة الثنائية العادية هو أن العقدة اليسرى BST لها قيمة أصغر من العقدة الجذرية، والعقدة اليمنى لها قيمة أكبر من العقدة الجذرية. لذلك، ستحتوي الشجرة الفرعية اليسرى دائمًا على قيمة أصغر من الجذر، وستحتوي الشجرة الفرعية اليمنى دائمًا على قيمة أكبر من الجذر.

الاختلافات بين الشجرة الثنائية وشجرة البحث الثنائية

مثال على أشجار البحث الثنائية

دعونا نأخذ المثال التالي لتوضيح مفاهيم شجرة البحث الثنائية.

مثال على أشجار البحث الثنائية

هنا يمكنك اتباع جميع العقد للانضباط المحدد. توجد صيغة للحد الأقصى لعدد العقد في شجرة البحث الثنائية. إذا لاحظنا الشجرة أعلاه، يمكننا أن نرى أن كل عقدة لديها طفلين باستثناء جميع العقد الورقية. وارتفاع (ح) الشجرة الثنائية المعطاة هو 4. الصيغة هي 2h - 1. لذلك يعطي 15.

مثال على أشجار البحث الثنائية

مثال على أشجار البحث الثنائية

الصورة المذكورة أعلاه ليست شجرة ثنائية كاملة أو شجرة ثنائية متوازنة، وتسمى الشجرة الثنائية الكاملة أو الشجرة الثنائية المتوازنة. هناك بنية بيانات أخرى تسمى AVL (نوع آخر من الشجرة الثنائية) تعمل على تحسين ارتفاع الشجرة الثنائية وإجراء البحث بشكل أسرع عن BST كما في الشكل 3.

حاول حساب عملية العبور بالترتيب للشجرة الثنائية المذكورة أعلاه. ستجد أن هذا سيعطي مصفوفة مرتبة غير متناقصة، وستكون خوارزميات العبور مماثلة لتلك الخاصة بالشجرة الثنائية.

أنواع الشجرة الثنائية

فيما يلي بعض الأنواع المهمة من الشجرة الثنائية:

  • شجرة ثنائية كاملة: يمكن أن تحتوي كل عقدة على 0 أو 2 عقد فرعية في هذه الشجرة الثنائية. غير مسموح بعقدة فرعية واحدة فقط في هذا النوع من الأشجار الثنائية. لذلك، باستثناء العقدة الورقية، سيكون لجميع العقد طفلان.

أنواع الشجرة الثنائية

  • شجرة ثنائية كاملة: يمكن أن تحتوي كل عقدة على 0 أو 2 عقدة. تبدو مثل الشجرة الثنائية الكاملة، ولكن جميع عناصر الورقة تميل إلى الشجرة الفرعية اليسرى، بينما في عقدة الشجرة الثنائية الكاملة يمكن أن تكون في الشجرة الفرعية اليمنى أو اليسرى.

أنواع الشجرة الثنائية

  • شجرة ثنائية مثالية: يجب أن تحتوي جميع العقد على 0 أو 2 عقدة، ويجب أن تكون جميع العقد الورقية على نفس المستوى أو الارتفاع. المثال أعلاه لبنية شجرة ثنائية كاملة ليس شجرة ثنائية مثالية لأن العقدة 6 والعقدة 1,2,3،XNUMX،XNUMX ليسا في نفس الارتفاع. لكن مثال الشجرة الثنائية الكاملة هو شجرة ثنائية مثالية.
  • شجرة ثنائية منحلة: يمكن لكل عقدة أن يكون لها طفل واحد فقط. كل العمليات مثل البحث والإدراج والحذف تستغرق وقتًا قدره 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

تطبيق الشجرة الثنائية

فيما يلي بعض التطبيقات الشائعة للشجرة الثنائية:

  • تنظيم بيانات العقدة بترتيب فرزها
  • تستخدم في الخريطة وتعيين كائنات العقدة في مكتبات لغات البرمجة.
  • البحث عن العناصر في هياكل البيانات

»تعرف على برنامجنا التعليمي التالي حول خوارزمية الجمع

الأسئلة الشائعة

عمق العقدة هو عدد الحواف الممتدة من الجذر إلى تلك العقدة. ارتفاع العقدة هو عدد الحواف على أطول مسار من العقدة إلى إحدى الأوراق. ارتفاع الشجرة يساوي ارتفاع الجذر.

تقتصر الشجرة الثنائية على عقدتين فرعيتين فقط، مما قد يؤدي إلى عدم توازنها وبطئها. أما شجرة AVL فهي شجرة بحث ثنائية متوازنة ذاتيًا، تحافظ على فرق الارتفاع بين الأشجار الفرعية ضمن نطاق واحد، مما يضمن تنفيذ عمليات من رتبة O(log n).

يمكن تخزين الشجرة الثنائية في مصفوفة بوضع الجذر عند الفهرس 0. بالنسبة للعقدة الموجودة عند الفهرس i، يكون ابنها الأيسر عند 2i+1 وابنها الأيمن عند 2i+2. وهذا يناسب الأشجار الثنائية الكاملة بشكل أفضل.

تستطيع أدوات الذكاء الاصطناعي تحويل قائمة من القيم أو التعليمات البرمجية إلى مخطط شجري ثنائي تفاعلي، مع إبراز العقد أثناء إدراجها أو البحث عنها أو حذفها. وهذا يساعد المبتدئين على فهم كيفية تغير البنية خلال كل عملية.

نعم. يمكن لمساعدي البرمجة بالذكاء الاصطناعي إنشاء أكواد الأشجار الثنائية وBST - الإضافة والبحث والحذف والاجتياز - بلغات مثل C++, Pythonو Java. Revقم بمراجعة واختبار المخرجات، لأن الذكاء الاصطناعي قد يغفل عن الحالات الشاذة مثل التكرارات.

تلخيص هذه التدوينة بـ: