Menu
Coddy logo textTech

Topological Sort (מיון טופולוגי)

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

מיון טופולוגי של גרף מכוון חסר מעגלים (DAG) הוא סידור ליניארי של הצמתים כך שלכל קשת u → v, הצומת u מופיע לפני v. הוא עונה על שאלות כמו "באיזה סדר אפשר להריץ את המשימות האלה כך שכל דרישה מקדימה תסתיים קודם?" לחצו על הפעלה למעלה כדי לראות את האלגוריתם של Kahn מקלף צמתים בסדר תקין.

האלגוריתם של Kahn לוקח שוב ושוב צומת שלא נשארו לו קשתות נכנסות (דרגת כניסה 0), מוסיף אותו לסדר ומסיר את הקשתות היוצאות ממנו, מה שעשוי לשחרר צמתים חדשים עם דרגת כניסה 0. הוא עובד רק על DAG: אם יש מעגל, חלק מהצמתים לעולם לא יגיעו לדרגת כניסה 0 ולא קיים סדר תקין. זמן הריצה שלו הוא O(V + E).

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

מדדסיבוכיותהערות
זמןO(V + E)כל צומת נפלט פעם אחת, כל קשת מוסרת פעם אחת
זיכרוןO(V)ספירות דרגת כניסה + קבוצת המוכנים + הסדר
דורשDAGלגרף עם מעגלים אין סדר טופולוגי
תוצאהלא יחידהיכולים להתקיים סדרים תקינים רבים

צעד אחר צעד (האלגוריתם של Kahn)

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

דוגמה מפורטת

מיון ה-DAG עם הקשתות A→C, B→C, C→D, C→E, D→F, E→F (דרגות כניסה התחלתיות A:0 B:0 C:2 D:1 E:1 F:2):

צעדקבוצת המוכניםסדרפעולה
0{A, B}[]ל-A ול-B יש דרגת כניסה 0 בהתחלה, ולכן שניהם מוכנים.
1{B}[A]פולטים את A; הקשת A→C מורידה את דרגת הכניסה של C מ-2 ל-1.
2{C}[A, B]פולטים את B; הקשת B→C מורידה את C מ-1 ל-0, ולכן C נעשה מוכן.
3{D, E}[A, B, C]פולטים את C; הקשתות C→D ו-C→E מורידות את D ואת E ל-0, ושניהם נעשים מוכנים.
4{E}[A, B, C, D]פולטים את D; הקשת D→F מורידה את דרגת הכניסה של F מ-2 ל-1.
5{F}[A, B, C, D, E]פולטים את E; הקשת E→F מורידה את F מ-1 ל-0, ולכן F נעשה מוכן.
6{}[A, B, C, D, E, F]פולטים את F; קבוצת המוכנים ריקה וכל 6 הצמתים מסודרים. סיימנו.

מתי להשתמש במיון טופולוגי

כדאי כאשרעדיף להימנע כאשר
צריך סדר שמכבד תלויות (שלבי בנייה, התקנת חבילות, דרישות קדם של קורסים).הגרף לא מכוון: סדר טופולוגי מוגדר רק לגרפים מכוונים.
הגרף הוא DAG ורוצים סידור ליניארי תקין כלשהו.הגרף עלול להכיל מעגלים ובכל זאת צריך סדר מלא (אין כזה).
רוצים לזהות מעגלים בזול: מיון טופולוגי שנכשל מוכיח שקיים מעגל.צריך את הסדר הקצר ביותר או האופטימלי לפי משקל כלשהו; מיון טופולוגי רגיל מתעלם ממשקלים.
מעבדים את הסדר פעם אחת ב-O(V + E).הקשתות משתנות כל הזמן וצריך למיין מחדש בכל עדכון, ושם מבנה אינקרמנטלי מתאים יותר.

קוד Topological Sort

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

קוד Topological Sort ב-Python

Python
1from collections import deque2
3
4def topological_sort(graph):5    # Kahn's algorithm: repeatedly remove nodes with no incoming edges6    in_degree = {node: 0 for node in graph}7    for node in graph:8        for neighbor in graph[node]:9            in_degree[neighbor] += 110    queue = deque(node for node in graph if in_degree[node] == 0)11    order = []12    while queue:13        node = queue.popleft()14        order.append(node)15        for neighbor in graph[node]:16            in_degree[neighbor] -= 117            if in_degree[neighbor] == 0:18                queue.append(neighbor)19    if len(order) != len(graph):20        raise ValueError("Graph has a cycle, no topological order")21    return order22
23
24graph = {25    "shirt": ["tie", "jacket"],26    "tie": ["jacket"],27    "pants": ["shoes", "jacket"],28    "socks": ["shoes"],29    "shoes": [],30    "jacket": [],31}32
33print(" -> ".join(topological_sort(graph)))
להריץ את הקוד הזה בעורך ה-Python אונליין

שאלות נפוצות על מיון טופולוגי

למה משמשים מיון טופולוגי?
הוא מסדר משימות כך שכל תלות מגיעה לפני מה שזקוק לה. שימושים אמיתיים כוללים מערכות בנייה ומנהלי חבילות (קודם מהדרים את התלויות), תזמון קורסים עם דרישות קדם, וסדר החישוב של נוסחאות בגיליון אלקטרוני.
מהי סיבוכיות הזמן של מיון טופולוגי?
גם האלגוריתם של Kahn וגם הגישה המבוססת על DFS רצים בזמן O(V + E), כי כל צומת מעובד פעם אחת וכל קשת נבדקת פעם אחת. הם משתמשים בזיכרון נוסף של O(V).
למה מיון טופולוגי דורש DAG?
מעגל מכוון יוצר סתירה: אם a חייב לבוא לפני b ו-b חייב לבוא לפני a, אף סדר ליניארי לא מקיים את שני התנאים. לכן סדר טופולוגי קיים אם ורק אם הגרף הוא גרף מכוון חסר מעגלים. האלגוריתם של Kahn מזהה מעגל כשהוא מסיים לפני שפלט את כל הצמתים.
מה ההבדל בין האלגוריתם של Kahn למיון טופולוגי עם DFS?
האלגוריתם של Kahn איטרטיבי ודומה ל-BFS: הוא מסיר שוב ושוב צמתים עם דרגת כניסה 0, וכך זיהוי המעגלים והסדר מפורשים לגמרי. גישת ה-DFS מבקרת בצמתים ברקורסיה ומוסיפה כל צומת לתחילת הסדר כשהרקורסיה שלו מסתיימת, כך שמתקבל ההפך של זמני הסיום. שתיהן O(V + E); Kahn נמנע מרקורסיה עמוקה ומציג את קבוצת המוכנים באופן טבעי, ואילו DFS קצר יותר לכתיבה פעמים רבות.
מתי להשתמש במיון טופולוגי במקום במיון רגיל?
השתמשו במיון טופולוגי כשהסדר נקבע לפי תלויות בין פריטים ולא לפי מפתח שאפשר להשוות. מיון השוואה רגיל כמו mergesort ב-O(n log n) מסדר לפי ערך; מיון טופולוגי מסדר לפי קשתות של "חייב לבוא לפני", ובניגוד למיון השוואה הוא יכול להפיק תשובות תקינות רבות לאותו קלט.
האם התוצאה של מיון טופולוגי יחידה?
בדרך כלל לא. בכל פעם ששניים או יותר צמתים מוכנים (דרגת כניסה 0) באותו זמן, אפשר לפלוט אותם בכל סדר, ולכן לרוב ה-DAG-ים יש כמה סדרים טופולוגיים תקינים. הסדר יחיד רק כשבכל צעד יש בדיוק צומת מוכן אחד, מה שקורה כשה-DAG יוצר שרשרת אחת (מסלול המילטוני).
איור של שפות התכנות ב-Coddy

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

להתחיל