מירי
כיתה יא · מחלקות ומבני נתונים

מחסנית ותור מי יוצא ראשון?

מחסנית היא כמו ערימת צלחות: מניחים למעלה ולוקחים מלמעלה. תור הוא כמו תור לקופה: מי שהגיע ראשון יוצא ראשון. כאן מכניסים ומוציאים ערכים משניהם באותו זמן ורואים את ההבדל, ובסוף בודקים סוגריים מאוזנים בעזרת מחסנית. כדאי לחזור קודם על רשימות בפייתון ועל מחלקות ועצמים.

1

הרעיון

מחסנית: LIFO

Last In, First Out: האחרון שנכנס יוצא ראשון. ברשימה של פייתון, append דוחף לסוף ו-pop() מוציא מהסוף.

stack = []
stack.append(1)
stack.append(2)
print(stack.pop())   # 2

תור: FIFO

First In, First Out: הראשון שנכנס יוצא ראשון. deque מ-collections מוציא מההתחלה מהר, עם popleft().

from collections import deque
queue = deque()
queue.append(1)
queue.append(2)
print(queue.popleft())   # 1

איפה משתמשים בהם

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

לזכור: במחסנית יש רק "ראש" אחד: נכנסים ויוצאים באותו צד. בתור יש שני צדדים: נכנסים מהסוף ויוצאים מההתחלה.
2

מעבדה: אותם ערכים, סדר יציאה אחר

התוכנית מכניסה את אותם מספרים גם למחסנית וגם לתור, ואז מוציאה מכל אחד. הזיזו את המחוונים כדי לבחור כמה מכניסים וכמה מוציאים. לחצו "שורה הבאה" וראו את הקופסאות זזות.

נסו את אלה:

הריצו בעצמכם

אפשר לשנות את הקוד ולהריץ פייתון אמיתי. בפעם הראשונה הטעינה לוקחת כמה שניות.

3

טעויות שכולם עושים

pop ממחסנית ריקה

pop על רשימה ריקה נותן IndexError: pop from empty list. בודקים לפני שמוציאים.

✗ עלול להיכשל

top = stack.pop()

✓ בודקים קודם

if stack:
    top = stack.pop()

בתור: pop(0), לא pop()

תור ברשימה רגילה צריך להוציא מההתחלה. pop() בלי מספר מוציא מהסוף, וזו כבר מחסנית.

✗ זו מחסנית

first = line.pop()

✓ תור

first = line.pop(0)

pop(0) איטי ברשימות ארוכות

pop(0) מזיז את כל שאר הערכים מקום אחד. בתור ארוך עדיף deque, שמוציא מההתחלה בלי להזיז כלום.

✗ איטי

line = []
first = line.pop(0)

✓ מהיר

line = deque()
first = line.popleft()

סוגריים: לא מספיק לספור

ב-([)] יש מספר שווה של פותחים וסוגרים, אבל הסדר שגוי. רק מחסנית תופסת את זה.

✗ רק סופרים

ok = text.count("(") == text.count(")")

✓ מחסנית

ok = balanced(text)
4

דוגמה פתורה: סוגריים מאוזנים

כל סוגר פותח נכנס למחסנית. כל סוגר סוגר צריך להתאים לפותח שבראש המחסנית, ואז מוציאים אותו. בסוף המחסנית צריכה להיות ריקה. בחרו מחרוזת ועברו צעד אחרי צעד.

5

תרגול


  

6

שאלות שכולם שואלים

מה ההבדל בין מחסנית לתור?

במחסנית האחרון שנכנס יוצא ראשון (LIFO), כמו ערימת צלחות. בתור הראשון שנכנס יוצא ראשון (FIFO), כמו תור לקופה. אם מכניסים 10, 20, 30, מחסנית תוציא קודם 30 ותור יוציא קודם 10. נסו במעבדה.

איך בודקים סוגריים מאוזנים עם מחסנית?

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

למה להשתמש ב-deque ולא ברשימה רגילה לתור?

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