Menu
Coddy logo textTech

Queue (תור)

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

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

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

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

עבור תור שמבוסס על חוצץ מעגלי או על רשימה מקושרת, שני המימושים הסטנדרטיים:

פעולהסיבוכיותהערות
EnqueueO(1)כותבים בסוף ומקדמים את אינדקס הסוף.
DequeueO(1)קוראים מההתחלה ומקדמים את אינדקס ההתחלה, בלי הזזות.
Peek (ראש התור)O(1)קוראים את הערך שבראש התור בלי להסיר אותו.
חיפושO(n)לא לזה נועד תור: צריך לרוקן אותו כדי להסתכל פנימה.
זיכרוןO(n)תא אחד לכל ערך שממתין.

צעד אחר צעד

צעדמה קורה
1התור מתחיל ריק, וההתחלה והסוף מצביעים על אותו תא.
2Enqueue כותב את הערך בסוף, ואז מקדם את הסוף באחד.
3כל enqueue נוסף נוחת מאחורי הערכים שכבר ממתינים.
4Dequeue קורא את הערך שבהתחלה, ואז מקדם את ההתחלה באחד.
5הערך שחוזר הוא תמיד זה שחיכה הכי הרבה זמן.
6כשההתחלה פוגשת את הסוף התור שוב ריק, ו-dequeue נוסף הוא שגיאה.

דוגמה מפורטת

הכנסת 3, 7, 5 לתור ואז ריקון שלו:

פעולהתור (מההתחלה לסוף)מחזיר
enqueue(3)[3]כלום
enqueue(7)[3, 7]כלום
enqueue(5)[3, 7, 5]כלום
dequeue()[7, 5]3, הערך הוותיק ביותר
dequeue()[5]7
dequeue()[]5, הערך החדש ביותר, אחרון

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

השתמשו בו כאשרהימנעו ממנו כאשר
העבודה חייבת להיות מטופלת לפי סדר ההגעה: תורי עבודות, חוצצי בקשות, מנהלי הדפסהאתם צריכים את הפריט האחרון ראשון, וזו מחסנית
אתם סורקים רמה אחר רמה, כמו שחיפוש לרוחב עושהפריטים צריכים לקבל שירות לפי עדיפות ולא לפי סדר הגעה, ושם ערימה מתאימה
יצרן וצרכן רצים במהירויות שונות וצריכים חוצץ ביניהםאתם צריכים לחפש או לגשת לפי אינדקס לאמצע הנתונים
אתם רוצים הכנסה והסרה של O(1) בלי להזיז איבריםהייתם מממשים אותו בהזזת מערך בכל dequeue, מה שהופך אותו ל-O(n)

קוד Queue

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

קוד Queue ב-Python

Python
1from collections import deque2
3queue = deque()4
5# Enqueue three values at the rear6for value in [3, 7, 5]:7    queue.append(value)8    print(f"enqueue {value} -> {list(queue)}")9
10# Dequeue them from the front: first in, first out11while queue:12    value = queue.popleft()13    print(f"dequeue {value} -> {list(queue)}")14
15print("empty:", len(queue) == 0)
להריץ את הקוד הזה בעורך ה-Python אונליין

שאלות נפוצות על תור

מה פירוש FIFO?
First in, first out, כלומר נכנס ראשון, יוצא ראשון: הערך שחיכה הכי הרבה זמן הוא הבא שמקבל שירות. תור בקופת כרטיסים הוא הדוגמה היומיומית. מחסנית היא השיטה ההפוכה, LIFO.
מה ההבדל בין תור למחסנית?
רק הקצה שממנו מסירים. שניהם מוסיפים בסוף ב-O(1); תור מסיר מההתחלה (FIFO), ומחסנית מסירה מאותו קצה שאליו הוסיפה (LIFO). מלבד זה טבלאות הסיבוכיות שלהם זהות.
מהן הפעולות העיקריות על תור?
enqueue מוסיף ערך בסוף, dequeue מסיר ומחזיר את הערך שבראש התור, peek (או front) קורא את ראש התור בלי להסיר אותו, ו-is_empty מדווח אם משהו ממתין. כל הארבע הן O(1).
למה dequeue איטי אם משתמשים במערך רגיל?
כי הסרת אינדקס 0 ממערך מזיזה כל איבר שנותר שמאלה, וכך כל dequeue הופך ל-O(n). מימושים אמיתיים נמנעים מזה בעזרת חוצץ מעגלי שמקדם אינדקס התחלה, או רשימה מקושרת עם מצביע ראש. collections.deque של Python ו-ArrayDeque של Java עושים את זה בשבילכם, ואילו list.pop(0) לא.
מהו תור מעגלי?
תור במערך בגודל קבוע, שבו אינדקסי ההתחלה והסוף חוזרים ל-0 כשהם מגיעים לקצה. הוא משתמש מחדש בתאים שהתפנו על ידי dequeue, כך שתור בקיבולת n ממשיך לעבוד ללא הגבלה במקום לצאת מקצה המערך.
איפה משתמשים בתורים בתוכניות אמיתיות?
תורי משימות והודעות בין שירותים, מנהלי הדפסה ועבודות, חוצצי בקשות בשרתי אינטרנט, חוצצי מקלדת ואירועים, צינורות יצרן-צרכן, וחיפוש לרוחב, שבו התור הוא מה שגורם למעבר להתקדם רמה אחר רמה.
איור של שפות התכנות ב-Coddy

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

להתחיל