Menu
Coddy logo textTech

Heap (ערימה בינארית)

עודכן לאחרונה

ערימה בינארית היא עץ בינארי שלם ששומר את הערך הקטן ביותר (ערימת מינימום) או הגדול ביותר (ערימת מקסימום) בשורש. ההדמיה הזו היא ערימת מינימום: כל הורה קטן מילדיו או שווה להם. כדי להוסיף ערך, מצרפים אותו בתא הפנוי הבא ואז "מעלים אותו" (sift up), כלומר מחליפים אותו עם ההורה שלו כל עוד הוא קטן יותר, עד שתכונת הערימה מתקיימת שוב. לחצו על הפעלה למעלה כדי לראות כל ערך חדש מבעבע למעלה למקומו.

מכיוון שערימה היא עץ שלם, היא נשמרת בצורה דחוסה במערך: הילדים של צומת i נמצאים ב-2i+1 וב-2i+2. הכנסה והסרת המינימום הן O(log n) (מסלול אחד משורש לעלה), ואילו הצצה במינימום היא O(1), בדיוק מה שתור עדיפויות צריך.

סיבוכיות זמן וזיכרון

פעולהסיבוכיותהערות
הצצה במינימום או במקסימוםO(1)זה תמיד השורש
הכנסה (push)O(log n)העלאה לאורך מסלול אחד
הסרת מינימום או מקסימוםO(log n)הורדה לאורך מסלול אחד
בניית ערימהO(n)heapify על הכל בבת אחת
זיכרוןO(n)מבוסס מערך, בלי מצביעים

צעד אחר צעד (push)

צעדמה קורה
1מצרפים את הערך החדש בסוף (העלה הפנוי הבא).
2משווים אותו להורה שלו.
3אם הוא קטן יותר (בערימת מינימום), מחליפים ומעלים אותו.
4חוזרים על כך עד שהוא כבר לא קטן מההורה שלו, או עד שהוא מגיע לשורש.

דוגמה מפורטת

בניית ערימת מינימום על ידי הכנסת [5, 3, 8, 1, 4] ערך אחד בכל פעם:

הכנסההמערך אחרי ההעלאהפעולה
5[5]הערך הראשון הופך לשורש.
3[3, 5]3 < ההורה 5, ולכן מחליפים ומעלים אותו לשורש.
8[3, 5, 8]8 > ההורה 5, ולכן הוא נשאר עלה.
1[1, 3, 8, 5]1 < ההורה 5, מחליפים; אחר כך 1 < ההורה 3, מחליפים עד לשורש.
4[1, 3, 8, 5, 4]4 > ההורה 3, ולכן הוא נשאר; המינימום 1 נשאר בשורש.

מתי להשתמש בערימה

השתמשו בה כאשרהימנעו ממנה כאשר
אתם צריכים שוב ושוב את הפריט הקטן או הגדול ביותר מקבוצה משתנה.אתם צריכים לחפש ערכים כלשהם, לא רק את הקיצוני: השתמשו ב-BST או ב-hash set.
אתם מממשים תור עדיפויות עבור Dijkstra, A* או מתזמן משימות.אתם צריכים שהנתונים יהיו ממוינים לגמרי בכל רגע.
אתם רוצים הכנסה והסרת מינימום של O(log n) עם פריסת מערך דחוסה.אתם צריכים חיפוש או הסרה מהירים של איבר מסוים (שאינו השורש).
אתם צריכים למזג זרם של פריטים ולשלוף אותם לפי עדיפות.כמות הנתונים זעירה וסריקה ליניארית פשוטה יותר ומהירה מספיק.

קוד Heap (Priority Queue)

מימוש נקי של Heap (Priority Queue) שאפשר להריץ, ב-Python, JavaScript, Java, C++, C. בחרו שפה, העתיקו את הקוד, או פתחו אותו טעון מראש בעורך האונליין של Coddy.

קוד Heap (Priority Queue) ב-Python

Python
1class MinHeap:2    def __init__(self):3        self.data = []4
5    def push(self, value):6        # Append at the end, then bubble up to restore order7        self.data.append(value)8        i = len(self.data) - 19        while i > 0:10            parent = (i - 1) // 211            if self.data[parent] <= self.data[i]:12                break13            self.data[i], self.data[parent] = self.data[parent], self.data[i]14            i = parent15
16    def pop(self):17        # Move the last leaf to the root, then sift it down18        top = self.data[0]19        last = self.data.pop()20        if self.data:21            self.data[0] = last22            self._sift_down(0)23        return top24
25    def _sift_down(self, i):26        n = len(self.data)27        while True:28            smallest = i29            left, right = 2 * i + 1, 2 * i + 230            if left < n and self.data[left] < self.data[smallest]:31                smallest = left32            if right < n and self.data[right] < self.data[smallest]:33                smallest = right34            if smallest == i:35                return36            self.data[i], self.data[smallest] = self.data[smallest], self.data[i]37            i = smallest38
39
40heap = MinHeap()41for value in [5, 3, 8, 1, 9, 2]:42    heap.push(value)43
44print("Heap array:     ", heap.data)45print("Popped in order:", [heap.pop() for _ in range(6)])
להריץ את הקוד הזה בעורך ה-Python אונליין

שאלות נפוצות על ערימה

למה משמשת ערימה?
ערימות מממשות תורי עדיפויות, שמניעים את אלגוריתם המסלול הקצר ביותר של Dijkstra, מתזמני משימות וסימולציות של אירועים. הן גם המנוע של heapsort. בכל פעם שצריך שוב ושוב את הפריט הקטן או הגדול ביותר מקבוצה משתנה, ערימה היא הכלי המתאים.
מה ההבדל בין ערימה לעץ חיפוש בינארי?
שניהם עצים בינאריים, אבל BST שומר על סדר ממוין מלא משמאל לימין (מה שמאפשר חיפוש מסודר), ואילו ערימה מבטיחה רק את היחס בין הורה לילד (מינימום או מקסימום בשורש). ערימה נותנת גישה של O(1) לערך הקיצוני; BST נותן חיפוש של O(log n) לכל ערך.
למה ערימה נשמרת במערך?
מכיוון שערימה היא תמיד עץ בינארי שלם, הצמתים שלה ממופים בדיוק לאינדקסים של מערך: הילדים של אינדקס i נמצאים ב-2i+1 וב-2i+2, וההורה ב-(i-1)/2. כך אין צורך לשמור מצביעים לילדים, והביצועים מול המטמון מצוינים.
האם ערימה זהה למערך ממוין?
לא. ערימה מבטיחה רק שכל הורה קטן (בערימת מינימום) או גדול (בערימת מקסימום) מהילדים שלו, כך שאחים ובני דודים אינם בשום סדר מסוים. מערך ממוין מסודר לגמרי אבל הכנסה אליו עולה O(n), ואילו ערימה מכניסה ב-O(log n) ועדיין נותנת גישה מיידית לערך הקיצוני.
מתי כדאי להשתמש בערימה במקום פשוט למיין מערך?
בחרו בערימה כשהנתונים ממשיכים להשתנות ואתם צריכים רק את המינימום או המקסימום הנוכחי: הכנסה ושליפה עולות O(log n) כל אחת, לעומת מיון מחדש של המערך כולו. אם יש לכם נתונים קבועים ואתם רוצים את כל האיברים בסדר, מיון יחיד של O(n log n) פשוט יותר ולעתים קרובות מהיר יותר.
האם בניית ערימה מ-n פריטים לוקחת O(n log n)?
לא אם בונים אותה בבת אחת. הכנסת n פריטים אחד אחד היא O(n log n), אבל heapify מלמטה למעלה, שמוריד צמתים מההורה האחרון ועד לשורש, רץ ב-O(n) בסך הכל, כי רוב הצמתים נמצאים קרוב לתחתית ויורדים רק מרחק קצר.
איור של שפות התכנות ב-Coddy

לשלוט באלגוריתמים עם Coddy

להתחיל