Binary Search Tree (עץ חיפוש בינארי)
עודכן לאחרונה
עץ חיפוש בינארי שומר את הערכים שלו בסדר ממוין: לכל צומת, כל הערכים בתת העץ השמאלי שלו קטנים ממנו וכל הערכים בתת העץ הימני שלו גדולים ממנו. כדי להכניס או למצוא ערך מתחילים בשורש ופונים שוב ושוב שמאלה או ימינה לפי ההשוואה, כך שכל צעד חוצה את מרחב החיפוש. לחצו על הפעלה למעלה כדי לראות ערכים מוצבים לפי השוואה וחיפוש שיורד לאורך העץ.
בעץ מאוזן הפעולות האלה רצות בזמן O(log n). המלכודת: הכנסת נתונים שכבר ממוינים הופכת את העץ לרשימה מקושרת עם פעולות O(n), ובדיוק בגלל זה קיימים עצים שמאזנים את עצמם כמו AVL ועצים אדומים-שחורים.
סיבוכיות זמן וזיכרון
| פעולה | מאוזן | המקרה הגרוע (עקום) |
|---|---|---|
| חיפוש | O(log n) | O(n) |
| הכנסה | O(log n) | O(n) |
| מחיקה | O(log n) | O(n) |
| זיכרון | O(n) | O(n) |
צעד אחר צעד (הכנסה)
| צעד | מה קורה |
|---|---|
| 1 | אם העץ ריק, הערך החדש הופך לשורש. |
| 2 | אחרת מתחילים בשורש. |
| 3 | אם הערך קטן יותר, עוברים לבן השמאלי; אם גדול יותר, פונים ימינה. |
| 4 | חוזרים על כך עד שמגיעים למקום ריק. |
| 5 | מחברים שם את הערך החדש כעלה. |
דוגמה מפורטת
הכנסת [5, 3, 8, 1, 4] לעץ ריק, ערך אחד בכל פעם:
| הכנסה | המסלול שנבחר | פעולה |
|---|---|---|
5 | - | העץ ריק, ולכן 5 הופך לשורש. |
3 | 5 | 3 < 5, פונים שמאלה; המקום ריק, מחברים את 3 כבן השמאלי של 5. |
8 | 5 | 8 > 5, פונים ימינה; המקום ריק, מחברים את 8 כבן הימני של 5. |
1 | 5 -> 3 | 1 < 5 פונים שמאלה, ואז 1 < 3 פונים שמאלה; מחברים את 1 כבן השמאלי של 3. |
4 | 5 -> 3 | 4 < 5 פונים שמאלה, ואז 4 > 3 פונים ימינה; מחברים את 4 כבן הימני של 3. |
מתי להשתמש בעץ חיפוש בינארי
| השתמשו בו כאשר | הימנעו ממנו כאשר |
|---|---|
| אתם צריכים סדר ממוין וגם חיפושים מהירים, וההכנסות מגיעות בסדר אקראי. | הנתונים מגיעים כבר ממוינים: BST לא מאוזן מידרדר ל-O(n) לכל פעולה. |
| אתם רוצים שמעבר in-order יחזיר את הערכים ברצף ממוין בלי עלות נוספת. | אתם צריכים רק בדיקות שייכות בלי סדר: טבלת גיבוב נותנת חיפוש של O(1) בממוצע. |
| אתם צריכים שאילתות טווח או את העוקב והקודם של מפתח. | אתם צריכים חסמים מובטחים של O(log n): בחרו במקום זאת עץ AVL או עץ אדום-שחור שמאזנים את עצמם. |
| הנתונים משתנים לעתים קרובות ועדכון מערך ממוין סטטי היה יקר. | הנתונים קבועים ולקריאה בלבד: מערך ממוין עם חיפוש בינארי פשוט יותר וידידותי למטמון. |
קוד Binary Search Tree
מימוש נקי של Binary Search Tree שאפשר להריץ, ב-Python, JavaScript, Java, C++, C. בחרו שפה, העתיקו את הקוד, או פתחו אותו טעון מראש בעורך האונליין של Coddy.
קוד Binary Search Tree ב-Python
1class Node:2 def __init__(self, key):3 self.key = key4 self.left = None5 self.right = None6
7
8def insert(node, key):9 if node is None:10 return Node(key)11 if key < node.key:12 node.left = insert(node.left, key)13 elif key > node.key:14 node.right = insert(node.right, key)15 return node # duplicates are ignored16
17
18def search(node, key):19 if node is None:20 return False21 if key == node.key:22 return True23 if key < node.key:24 return search(node.left, key)25 return search(node.right, key)26
27
28def inorder(node):29 if node is None:30 return []31 return inorder(node.left) + [node.key] + inorder(node.right)32
33
34root = None35for key in [8, 3, 10, 1, 6, 14, 4, 7]:36 root = insert(root, key)37
38print("Inorder (sorted):", inorder(root))39print("search(6): ", search(root, 6))40print("search(5): ", search(root, 5))קוד Binary Search Tree ב-JavaScript
1class Node {2 constructor(value) {3 this.value = value;4 this.left = null;5 this.right = null;6 }7}8
9class BinarySearchTree {10 constructor() {11 this.root = null;12 }13
14 insert(value) {15 const attach = (node) => {16 if (!node) return new Node(value);17 if (value < node.value) node.left = attach(node.left);18 else node.right = attach(node.right);19 return node;20 };21 this.root = attach(this.root);22 }23
24 search(value) {25 let current = this.root;26 while (current) {27 if (value === current.value) return true;28 current = value < current.value ? current.left : current.right;29 }30 return false;31 }32
33 // Inorder traversal of a BST visits values in sorted order34 inorder(node = this.root, out = []) {35 if (!node) return out;36 this.inorder(node.left, out);37 out.push(node.value);38 this.inorder(node.right, out);39 return out;40 }41}42
43const bst = new BinarySearchTree();44for (const value of [8, 3, 10, 1, 6, 14, 4]) bst.insert(value);45console.log("Inorder:", bst.inorder().join(" "));46console.log("search(6):", bst.search(6));47console.log("search(7):", bst.search(7));קוד Binary Search Tree ב-Java
1public class Main {2 static class Node {3 int key;4 Node left, right;5 Node(int key) { this.key = key; }6 }7
8 static Node insert(Node node, int key) {9 if (node == null) return new Node(key);10 // Smaller keys go left, larger keys go right11 if (key < node.key) node.left = insert(node.left, key);12 else if (key > node.key) node.right = insert(node.right, key);13 return node;14 }15
16 static boolean search(Node node, int key) {17 if (node == null) return false;18 if (key == node.key) return true;19 return key < node.key ? search(node.left, key) : search(node.right, key);20 }21
22 static void inorder(Node node, StringBuilder sb) {23 if (node == null) return;24 inorder(node.left, sb);25 sb.append(node.key).append(" ");26 inorder(node.right, sb);27 }28
29 public static void main(String[] args) {30 Node root = null;31 int[] keys = {8, 3, 10, 1, 6, 14, 4};32 for (int k : keys) root = insert(root, k);33
34 StringBuilder sb = new StringBuilder();35 inorder(root, sb);36 System.out.println("Inorder (sorted): " + sb.toString().trim());37 System.out.println("search 6: " + search(root, 6));38 System.out.println("search 7: " + search(root, 7));39 }40}קוד Binary Search Tree ב-C++
1#include <iostream>2
3struct Node {4 int value;5 Node* left = nullptr;6 Node* right = nullptr;7 explicit Node(int v) : value(v) {}8};9
10Node* insert(Node* root, int value) {11 if (root == nullptr) return new Node(value);12 if (value < root->value) root->left = insert(root->left, value);13 else if (value > root->value) root->right = insert(root->right, value);14 return root; // duplicates are ignored15}16
17bool contains(const Node* root, int value) {18 // Walk down: smaller goes left, larger goes right19 while (root != nullptr) {20 if (value == root->value) return true;21 root = value < root->value ? root->left : root->right;22 }23 return false;24}25
26void inorder(const Node* node) {27 if (node == nullptr) return;28 inorder(node->left);29 std::cout << node->value << " ";30 inorder(node->right);31}32
33int main() {34 Node* root = nullptr;35 for (int value : {50, 30, 70, 20, 40, 60, 80}) {36 root = insert(root, value);37 }38 std::cout << "Sorted: ";39 inorder(root);40 std::cout << "\n" << std::boolalpha;41 std::cout << "contains(40): " << contains(root, 40) << "\n";42 std::cout << "contains(45): " << contains(root, 45) << "\n";43 return 0;44}קוד Binary Search Tree ב-C
1#include <stdbool.h>2#include <stdio.h>3#include <stdlib.h>4
5typedef struct Node {6 int value;7 struct Node* left;8 struct Node* right;9} Node;10
11Node* newNode(int value) {12 Node* n = malloc(sizeof(Node));13 n->value = value;14 n->left = n->right = NULL;15 return n;16}17
18Node* insert(Node* root, int value) {19 if (root == NULL) return newNode(value);20 if (value < root->value) root->left = insert(root->left, value);21 else if (value > root->value) root->right = insert(root->right, value);22 return root; // duplicates are ignored23}24
25bool contains(const Node* root, int value) {26 // Walk down: smaller goes left, larger goes right27 while (root != NULL) {28 if (value == root->value) return true;29 root = value < root->value ? root->left : root->right;30 }31 return false;32}33
34void inorder(const Node* node) {35 if (node == NULL) return;36 inorder(node->left);37 printf("%d ", node->value);38 inorder(node->right);39}40
41int main(void) {42 int values[] = {50, 30, 70, 20, 40, 60, 80};43 Node* root = NULL;44 for (int i = 0; i < 7; i++) root = insert(root, values[i]);45 printf("Sorted: ");46 inorder(root);47 printf("\n");48 printf("contains(40): %s\n", contains(root, 40) ? "true" : "false");49 printf("contains(45): %s\n", contains(root, 45) ? "true" : "false");50 return 0;51}שאלות נפוצות על עץ חיפוש בינארי
מהי סיבוכיות הזמן של עץ חיפוש בינארי?
O(log n) בעץ מאוזן, כי כל השוואה זורקת חצי מהצמתים שנותרו. במקרה הגרוע, עץ שהפך לשרשרת עקומה בגלל הכנסות ממוינות, הם מידרדרים ל-O(n).מה ההבדל בין עץ בינארי לעץ חיפוש בינארי?
למה עץ חיפוש בינארי יכול להפוך לאיטי?
O(n) לכל פעולה. עצים שמאזנים את עצמם (AVL, אדום-שחור) מסובבים צמתים כדי למנוע את זה.מה ההבדל בין עץ חיפוש בינארי לטבלת גיבוב?
O(1) בממוצע, אבל שומרת את המפתחות בלי סדר מסוים, ולכן היא לא יכולה לענות על שאילתות טווח או עוקב. עץ חיפוש בינארי איטי מעט יותר, O(log n), אבל שומר את המפתחות מסודרים, כך שאפשר לעבור עליהם בסדר ממוין ולמצוא ערכים קרובים. בחרו ב-BST כשהסדר חשוב, ובטבלת גיבוב כשצריך רק בדיקות שייכות.מתי כדאי להשתמש בעץ שמאזן את עצמו במקום ב-BST רגיל?
O(log n). BST רגיל מתאים ללימוד, לכמויות נתונים קטנות, או כשהמפתחות מגיעים בסדר אקראי, אבל הוא לא מציע שום הגנה מפני המקרה הגרוע העקום.