Binary Tree (עץ בינארי)
עודכן לאחרונה
עץ בינארי הוא היררכיה שבה לכל צומת יש לכל היותר שני ילדים, שנקראים הבן השמאלי והבן הימני. הצומת העליון הוא השורש, צמתים בלי ילדים הם עלים, ומספר הקשתות מהשורש ועד העלה העמוק ביותר הוא גובה העץ. בניגוד לעץ חיפוש בינארי, לעץ בינארי רגיל אין כלל סדר: זו רק הצורה. לחצו על הפעלה למעלה כדי לראות עץ מתמלא רמה אחר רמה, ואז עובר מעבר in-order (שמאל, צומת, ימין).
עצים בינאריים הם הבסיס למבנים רבים: עצי חיפוש בינאריים, ערימות, עצי ביטויים ועוד. מעברים מבקרים בכל צומת בסדר מוגדר: in-order, pre-order ו-post-order הם שלושת המעברים לעומק, וכל אחד מהם שימושי למשימות אחרות.
מונחים
| מונח | משמעות |
|---|---|
| שורש | הצומת העליון, שאין לו הורה |
| עלה | צומת בלי ילדים |
| גובה | המסלול הארוך ביותר משורש לעלה (בקשתות) |
| עומק | המרחק של צומת מהשורש |
| שלם | כל הרמות מלאות, חוץ אולי מהאחרונה, שמתמלאת משמאל לימין |
שלושת המעברים לעומק
| מעבר | סדר | שימוש נפוץ |
|---|---|---|
| In-order | שמאל, צומת, ימין | פלט ממוין של BST |
| Pre-order | צומת, שמאל, ימין | העתקה או סריאליזציה של עץ |
| Post-order | שמאל, ימין, צומת | מחיקה או חישוב של עץ |
דוגמה מפורטת
מעבר in-order על העץ שנבנה מ-[4, 2, 6, 1, 3, 5] (מתמלא רמה אחר רמה):
| צעד | בצומת | פעולה |
|---|---|---|
| 1 | 4 | נכנסים ברקורסיה לתת העץ השמאלי של 4 לפני הביקור בו |
| 2 | 2 | נכנסים ברקורסיה לתת העץ השמאלי של 2 לפני הביקור בו |
| 3 | 1 | עלה, אין בן שמאלי: מבקרים ב-1, הפלט [1] |
| 4 | 2 | השמאל הסתיים: מבקרים ב-2, הפלט [1, 2], ואז נכנסים ברקורסיה ימינה |
| 5 | 3 | עלה: מבקרים ב-3, הפלט [1, 2, 3] |
| 6 | 4 | תת העץ השמאלי הסתיים: מבקרים ב-4, הפלט [1, 2, 3, 4], ואז נכנסים ברקורסיה ימינה |
| 7 | 5 | הבן השמאלי של 6, עלה: מבקרים ב-5, הפלט [1, 2, 3, 4, 5] |
| 8 | 6 | השמאל הסתיים, אין בן ימני: מבקרים ב-6, הפלט [1, 2, 3, 4, 5, 6] |
מתי להשתמש בעץ בינארי
| השתמשו בו כאשר | הימנעו ממנו כאשר |
|---|---|
| אתם צריכים לייצג נתונים היררכיים מטבעם (מערכות קבצים, עצי ביטויים, DOM) | הנתונים שטוחים ורשימה או מערך יספיקו: עץ רק מוסיף תקורה |
| אתם רוצים פעולות מסודרות ויכולים לשמור על איזון (BST או עץ שמאזן את עצמו) | אתם צריכים חיפוש לפי מפתח של O(1) בממוצע: טבלת גיבוב מנצחת כל עץ |
| אתם צריכים עיבוד in-order, pre-order או post-order של נתונים מובנים | צמתים מוכנסים בסדר ממוין ל-BST לא מאוזן: הוא מידרדר לרשימה של O(n) |
| שאילתות טווח או מעבר מסודר חשובים, ואת זה טבלאות גיבוב לא יכולות לספק | הזיכרון מוגבל: כל צומת נושא שני מצביעים לילדים בנוסף לנתונים |
קוד Binary Tree
מימוש נקי של Binary Tree שאפשר להריץ, ב-Python, JavaScript, Java, C++, C. בחרו שפה, העתיקו את הקוד, או פתחו אותו טעון מראש בעורך האונליין של Coddy.
קוד Binary Tree ב-Python
1from collections import deque2
3
4class Node:5 def __init__(self, value):6 self.value = value7 self.left = None8 self.right = None9
10
11def insert(root, value):12 # Level-order insert: fill the first empty child slot found13 if root is None:14 return Node(value)15 queue = deque([root])16 while queue:17 node = queue.popleft()18 if node.left is None:19 node.left = Node(value)20 return root21 queue.append(node.left)22 if node.right is None:23 node.right = Node(value)24 return root25 queue.append(node.right)26
27
28def inorder(node):29 if node is None:30 return []31 return inorder(node.left) + [node.value] + inorder(node.right)32
33
34root = None35for value in [1, 2, 3, 4, 5, 6, 7]:36 root = insert(root, value)37
38print("Root: ", root.value)39print("Inorder:", inorder(root))קוד Binary Tree ב-JavaScript
1class Node {2 constructor(value) {3 this.value = value;4 this.left = null;5 this.right = null;6 }7}8
9class BinaryTree {10 constructor() {11 this.root = null;12 }13
14 // Insert at the first free spot, top to bottom (level order)15 insert(value) {16 const node = new Node(value);17 if (!this.root) {18 this.root = node;19 return;20 }21 const queue = [this.root];22 while (queue.length > 0) {23 const current = queue.shift();24 if (!current.left) {25 current.left = node;26 return;27 }28 if (!current.right) {29 current.right = node;30 return;31 }32 queue.push(current.left, current.right);33 }34 }35
36 inorder(node = this.root, out = []) {37 if (!node) return out;38 this.inorder(node.left, out);39 out.push(node.value);40 this.inorder(node.right, out);41 return out;42 }43}44
45const tree = new BinaryTree();46for (const value of [1, 2, 3, 4, 5, 6, 7]) tree.insert(value);47console.log("Inorder traversal:", tree.inorder().join(" "));קוד Binary Tree ב-Java
1import java.util.ArrayDeque;2import java.util.Queue;3
4public class Main {5 static class Node {6 int value;7 Node left, right;8 Node(int value) { this.value = value; }9 }10
11 static Node root;12
13 // Insert at the first free slot in level order (keeps the tree complete)14 static void insert(int value) {15 Node node = new Node(value);16 if (root == null) { root = node; return; }17 Queue<Node> queue = new ArrayDeque<>();18 queue.add(root);19 while (!queue.isEmpty()) {20 Node cur = queue.poll();21 if (cur.left == null) { cur.left = node; return; }22 if (cur.right == null) { cur.right = node; return; }23 queue.add(cur.left);24 queue.add(cur.right);25 }26 }27
28 // Inorder: left subtree, node, right subtree29 static void inorder(Node node, StringBuilder sb) {30 if (node == null) return;31 inorder(node.left, sb);32 sb.append(node.value).append(" ");33 inorder(node.right, sb);34 }35
36 public static void main(String[] args) {37 for (int v = 1; v <= 7; v++) insert(v);38 StringBuilder sb = new StringBuilder();39 inorder(root, sb);40 System.out.println("Root: " + root.value);41 System.out.println("Inorder: " + sb.toString().trim());42 }43}קוד Binary 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 root->right = insert(root->right, value);14 return root;15}16
17// In-order traversal: left subtree, node, right subtree18void inorder(const Node* node) {19 if (node == nullptr) return;20 inorder(node->left);21 std::cout << node->value << " ";22 inorder(node->right);23}24
25int countNodes(const Node* node) {26 if (node == nullptr) return 0;27 return 1 + countNodes(node->left) + countNodes(node->right);28}29
30int main() {31 Node* root = nullptr;32 for (int value : {8, 3, 10, 1, 6, 14, 4}) {33 root = insert(root, value);34 }35 std::cout << "In-order: ";36 inorder(root);37 std::cout << "\n";38 std::cout << "Node count: " << countNodes(root) << "\n";39 return 0;40}קוד Binary Tree ב-C
1#include <stdio.h>2#include <stdlib.h>3
4typedef struct Node {5 int value;6 struct Node* left;7 struct Node* right;8} Node;9
10Node* newNode(int value) {11 Node* n = malloc(sizeof(Node));12 n->value = value;13 n->left = n->right = NULL;14 return n;15}16
17Node* insert(Node* root, int value) {18 if (root == NULL) return newNode(value);19 if (value < root->value) root->left = insert(root->left, value);20 else root->right = insert(root->right, value);21 return root;22}23
24// In-order traversal: left subtree, node, right subtree25void inorder(const Node* node) {26 if (node == NULL) return;27 inorder(node->left);28 printf("%d ", node->value);29 inorder(node->right);30}31
32int countNodes(const Node* node) {33 if (node == NULL) return 0;34 return 1 + countNodes(node->left) + countNodes(node->right);35}36
37int main(void) {38 int values[] = {8, 3, 10, 1, 6, 14, 4};39 Node* root = NULL;40 for (int i = 0; i < 7; i++) root = insert(root, values[i]);41 printf("In-order: ");42 inorder(root);43 printf("\n");44 printf("Node count: %d\n", countNodes(root));45 return 0;46}שאלות נפוצות על עץ בינארי
מה ההבדל בין עץ בינארי לעץ חיפוש בינארי?
מהו מעבר in-order?
מהו עץ בינארי שלם?
מתי כדאי להשתמש בעץ בינארי במקום במערך או בטבלת גיבוב?
O(1) בממוצע בטבלת גיבוב מנצחת עץ, ועבור נתונים שטוחים מערך פשוט יותר וידידותי יותר למטמון.מה ההבדל בין הגובה לעומק של עץ בינארי?
0, וגם העומק של הצומת היחיד שלו הוא 0.למה עץ בינארי לא מאוזן מתפקד כל כך גרוע?
O(h), כאשר h הוא הגובה. כשמפתחות מוכנסים בסדר ממוין העץ הופך לקו ישר, כך ש-h גדל עד n וכל פעולה מידרדרת ל-O(n), לא יותר טוב מרשימה מקושרת. עצים שמאזנים את עצמם כמו AVL או עצים אדומים-שחורים שומרים על גובה של O(log n) כדי למנוע את זה.