شجرة بحث ثنائية
صنف فرعي من | |
---|---|
البداية | |
يدرسه | |
المكتشف أو المخترع | |
زمن الاكتشاف أو الاختراع | |
أسوأ حالة تعقيد مكاني | |
متوسط التعقيد المكاني |
في علم الحاسوب، شجرة بحث ثنائية (بالإنجليزية: Binary Search Tree) اختصارًا (BST)، والتي يطلق عليها أيضًا أحيانًا شجرة بحث ثنائية مرتبة أو منظمة، هي شجرة ثنائية التي لديها هذه الخصائص التالية: [2]
- الشجرة الجزئية اليسرى لعقدة ما تحتوي فقط على عقد مع قيم أقل من قمية العقدة نفسها.
- الشجرة الجزئية اليمنى لعقدة ما تحتوي فقط على عقد مع قيم أكبر من قمية العقدة نفسها.
- كلتا الشجرتين الجزئيتين اليمنى واليسرى هما شجرتا بحث ثنائيتان.
بشكل عام، المعلومات التي تمثلها كل عقدة هي سجلات وليست فقط عناصر بيانات أحادية.
الميزة الرئيسية لأشجار البحث الثنائيين على بنى بيانات هي أن خوارزميات الترتيب وخوارزميات البحث المتعلقة بالإمكان أن تكون كفؤ جدا.
عمليات
[عدل]العمليات على شجرة البحث الثنائية تطلب مقارنات بين العقد. هذه المقارنات تتم عن طريق استدعاء مقارن (comparator)، والذي هو عبارة عن دالة التي تحسب الترتيب لأي قيمتين. ويكون معرفا حسب اللغة المنفذة بها الشجرة.
بحث
[عدل]البحث في شجرة بحث ثنائية على قيمة معينة يمكن أن يكون عوديا (recursive) أو تكرايا (iterative). الشرح هنا يغطي الطريقة العودية.
نبدأ بفحص العقدة الجذر. إذا كانت الشجرة null، إذن القيمة المراد البحث عنها لا توجد في الشجرة. وإلا، إذا كانت القيمة مساوية للجذر، ينجح البحث. إذا كانت القيمة أصغر من الجذر، نبحث في الشجرة الفرعية اليسرى. بصورة مشابهة، إذا كانت أكبر من الجذر، نبحث في الشجرة الفرعية اليمنى. نكرر هذه العملية حتى يتم إيجاد القيمة أو نصل إلا شجرة فرعية null. إذا لم يتم إيجاد القيمة المراد البحث عنها قبل أن نصل إلى شجرة فرعية null، إذن العنصر غير موجود في الشجرة.
هذه هي خوارزمية البحث في لغة بايثون:
# 'node' refers to the parent-node in this case def search_binary_tree(node, key): if node is None: return None # key not found if key < node.key: return search_binary_tree(node.leftChild, key) elif key > node.key: return search_binary_tree(node.rightChild, key) else: # key is equal to node key return node.value # found key
أو بلغة هاسكل:
searchBinaryTree _ NullNode = Nothing searchBinaryTree key (Node nodeKey nodeValue (leftChild, rightChild)) = case compare key nodeKey of LT -> searchBinaryTree key leftChild GT -> searchBinaryTree key rightChild EQ -> Just nodeValue
هذه العملية تطلب زمن (O(log n بالحالة المتوسطة، ولكن تطلب (O(n بأسوء حالة.
على افتراض أن BinarySearchTree
هي كلاس يحتوي على دالة (search(int
ومؤشر إلى العقدة الجذر، الخوارزمية أيضا منفذة بالطريقة التكرارية.
bool BinarySearchTree::search(int val) { Node *next = this->root(); while (next != NULL) { if (val == next->value()) { return true; } else if (val < next->value()) { next = next->left(); } else { next = next->right(); } } // غير موجود return false; }
إدخال
[عدل]يبدأ الإدخال كما يبدأ البحث; إذا لم تكن القيمة المراد إدخالها مساوية للجذر، نبحث في الشجرة الفرعية اليسرى أو اليمنى كما من قبل. أخيرا، سنصل إلى عقدة خارجية ونضيف القيمة كابن يميني أو يساري، اعتمادا على قيمة العقدة. وبعبارة أخرى، نفحص الجذر وندخل العقدة الجديدة في الشجرة الفرعية اليسرى إذا كانت قيمتها أصغر من قمية الجذر، أو في الشجرة الفرعية اليمنى إذا كانت قيمتها أكبر من قيمة الجذر.
هكذا يمكن تنفيذ إدخال في شجرة البحث الثنائية بسي++:
/* Inserts the node pointed to by "newNode" into the subtree rooted at "treeNode" */ void InsertNode(Node* &treeNode, Node *newNode) { if (treeNode == NULL) treeNode = newNode; else if (newNode->key < treeNode->key) InsertNode(treeNode->left, newNode); else InsertNode(treeNode->right, newNode); }
وهنا تنفيذ بالطريقة التكرارية للإدخال في شجرة البحث الثنائية في جافا:
private Node m_root; public void insert(int data) { if (m_root == null) { m_root = new TreeNode(data, null, null); return; } Node root = m_root; while (root != null) { // Not the same value twice if (data == root.getData()) { return; } else if (data < root.getData()) { // insert left if (root.getLeft() == null) { root.setLeft(new TreeNode(data, null, null)); return; } else { root = root.getLeft(); } } else { // insert right if (root.getRight() == null) { root.setRight(new TreeNode(data, null, null)); return; } else { root = root.getRight(); } } } }
وهنا تنفيذ بالطريقة العودية للإدخال:
private Node m_root; public void insert(int data){ if (m_root == null) { m_root = TreeNode(data, null, null); }else{ internalInsert(m_root, data); } } private static void internalInsert(Node node, int data){ // Not the same value twice if (data == node.getValue()) { return; } else if (data < node.getValue()) { if (node.getLeft() == null) { node.setLeft(new TreeNode(data, null, null)); }else{ internalInsert(node.getLeft(), data); } }else{ if (node.getRight() == null) { node.setRight(new TreeNode(data, null, null)); }else{ internalInsert(node.getRight(), data); } } }
ترتيب
[عدل]بالإمكان استخدام شجرة البحث الثنائية لتنفيذ خوارزمية بحث كفئة. ندخل كل القيم المراد ترتيها إلى شجرة بحث ثنائية، وبعد ذلك نمر على الشجرة بالترتيب بانين النتيجة:
def build_binary_tree(values): tree = None for v in values: tree = binary_tree_insert(tree, v) return tree def get_inorder_traversal(root): ''' Returns a list containing all the values in the tree, starting at *root*. Traverses the tree in-order(leftChild, root, rightChild). ''' result = [] traverse_binary_tree(root, lambda element: result.append(element)) return result
مراجع
[عدل]- ^ ا ب بول إي. بلاك. "قاموس الخوارزميات وهياكل البيانات" (بالإنجليزية). المعهد الوطني للمعايير والتقانة. Retrieved 2015-04-11.
- ^ Gilberg، R.؛ Forouzan، B. (2001)، "8"، Data Structures: A Pseudocode Approach With C++، Pacific Grove, CA: Brooks/Cole، ص. 339، ISBN:0-534-95216-X، مؤرشف من الأصل في 2020-04-17
انظر أيضًا
[عدل]- شجرة (بنية بيانات)
- شجرة ثنائية
- بنية بيانات
- خوارزمية بحث
- شجرة التحليل
- شجرة فنويك (تُعرف أيضاً باسم الشجرة المفهرسة الثنائية).