Smart-World Surf

יחידה 10: רשימות מקושרות

לימוד מבנה הנתונים רשימה מקושרת וסוגיה השונים.

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

מבוא לרשימות מקושרות

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

רשימה מקושרת (Linked List): מבנה נתונים לינארי שבו האלמנטים אינם מאוחסנים במיקומים רציפים בזיכרון. במקום זאת, כל אלמנט (צומת) מכיל את הנתון והפניה לצומת הבא.
צומת (Node): יחידת הבסיס ברשימה מקושרת. מכילה נתון והפניה לצומת הבא (ולעיתים גם לצומת הקודם).
מצביע/הפניה (Pointer/Reference): משתנה המכיל כתובת זיכרון של אובייקט אחר. ברשימה מקושרת, הוא מקשר בין צמתים.
ראש הרשימה (Head): הפניה לצומת הראשון ברשימה. חיוני לגישה לרשימה כולה.
זנב הרשימה (Tail): הפניה לצומת האחרון ברשימה. שימושי להוספה מהירה בסוף הרשימה.

השוואה למערכים

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

סוגי רשימות מקושרות

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

רשימה מקושרת חד-כיוונית (Singly Linked List)

הסוג הפשוט ביותר. כל צומת מכיל נתון והפניה לצומת הבא בלבד. הצומת האחרון מצביע ל-null. מאפשרת מעבר רק בכיוון אחד (קדימה) מהראש לזנב.

רשימה מקושרת דו-כיוונית (Doubly Linked List)

כל צומת מכיל נתון, הפניה לצומת הבא (next) והפניה לצומת הקודם (prev). הצומת הראשון מצביע ל-null ב-prev והאחרון ל-null ב-next. מאפשרת מעבר בשני הכיוונים, מה שמקל על מחיקות והוספות מסוימות.

רשימה מקושרת מעגלית (Circular Linked List)

יכולה להיות חד-כיוונית או דו-כיוונית. ההבדל הוא שהצומת האחרון מצביע בחזרה לצומת הראשון (במקום ל-null). שימושית במצבים בהם יש צורך במעבר מחזורי על האלמנטים, כמו תור משימות או הקצאת משאבים.

פעולות יסוד וניתוח סיבוכיות

הבנת הפעולות הבסיסיות וניתוח סיבוכיותן חיונית לתכנון יעיל ולפתרון בעיות במבחן.

סיבוכיות זמן (Time Complexity): מדד לכמות הזמן שלוקחת פעולה מסוימת כפונקציה של גודל הקלט (N), בדרך כלל במונחי Big O.

פעולות נפוצות:

  • הוספה (Insertion):
    • בראש הרשימה: O(1). יוצרים צומת חדש, מצביעים מראש הרשימה אליו, והצומת החדש מצביע לצומת שהיה קודם ראש הרשימה.
    • בסוף הרשימה: O(n) ברשימה חד-כיוונית ללא הפניה ל-tail (יש לעבור על כולה). O(1) אם יש הפניה ל-tail.
    • באמצע הרשימה: O(n) למציאת המיקום, O(1) לשינוי ההפניות.
  • מחיקה (Deletion):
    • בראש הרשימה: O(1). מזיזים את הפניית ה-head לצומת הבא.
    • בסוף הרשימה: O(n) ברשימה חד-כיוונית (יש למצוא את הצומת שלפני האחרון). O(1) ברשימה דו-כיוונית עם tail.
    • באמצע הרשימה: O(n) למציאת הצומת והצומת הקודם לו, O(1) לשינוי ההפניות.
  • חיפוש (Search): O(n). יש לעבור על הרשימה צומת אחר צומת עד למציאת האלמנט או הגעה לסוף הרשימה.
  • מעבר (Traversal): O(n). ביקור בכל צומת ברשימה, בדרך כלל מה-head ועד ל-null (או חזרה ל-head במעגלית).

היבטים קריטיים וטעויות נפוצות

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

כינוי (Aliasing): מצב שבו שני משתני הפניה (או יותר) מצביעים לאותו אובייקט בזיכרון. שינוי באמצעות הפניה אחת ישפיע על כל שאר ההפניות לאותו אובייקט.

טיפול בהפניות (Pointer Manipulation)

הטיפול הנכון בהפניות הוא לב ליבה של עבודה עם רשימות מקושרות. טעות נפוצה היא לאבד הפניה לצומת, מה שמוביל לאובדן נתונים או ל"דליפת זיכרון" (memory leak) אם אין יותר הפניות לאותו אובייקט.

מניפולציה נכונה של הפניות: בעת הוספה או מחיקה של צמתים, במיוחד בראש או בזנב הרשימה, יש לוודא שכל ההפניות הרלוונטיות (head, tail, next, prev) מתעדכנות בסדר הנכון. טעות בסדר הפעולות עלולה לגרום לאובדן חלק מהרשימה (ניתוק צמתים) או ליצירת לולאות אינסופיות. שימו לב במיוחד למקרי קצה: רשימה ריקה, רשימה עם צומת אחד, הוספה/מחיקה של הצומת הראשון/אחרון. אלו נקודות כשל נפוצות במבחנים.

רקורסיה ברשימות מקושרות

רבות מהפעולות על רשימות מקושרות (כמו הדפסה, היפוך, או חיפוש) ניתנות למימוש אלגנטי באמצעות רקורסיה, במיוחד ברשימות חד-כיווניות. עם זאת, יש לשים לב לעלות של קריאות רקורסיביות (stack space) ולמנוע StackOverflowError ברשימות ארוכות מאוד.

מקרי קצה (Edge Cases)

תמיד בדקו את הקוד שלכם עבור המקרים הבאים, שכן הם מהווים מלכודות נפוצות:

  • רשימה ריקה (head == null).
  • רשימה עם צומת אחד.
  • הוספה/מחיקה של הצומת הראשון.
  • הוספה/מחיקה של הצומת האחרון.

שאלות לדיון

  • באילו תרחישים רשימה מקושרת תהיה עדיפה על מערך, ומתי מערך יהיה עדיף? נמקו והביאו דוגמאות.
  • כיצד תשפיע טעות בטיפול בהפניות (aliasing) על תקינות רשימה מקושרת? תארו דוגמה ספציפית.
  • הסבירו את היתרונות והחסרונות של רשימה מקושרת דו-כיוונית לעומת חד-כיוונית. מתי נבחר בכל אחת מהן?
  • תארו אלגוריתם רקורסיבי להיפוך רשימה מקושרת חד-כיוונית. מהי סיבוכיות הזמן והמקום שלו?
  • כיצד ניתן לממש תור (Queue) באמצעות רשימה מקושרת, ומהי סיבוכיות הפעולות enqueue ו-dequeue במימוש זה?

נקודות לתשובת מודל

  • השוואה למערכים:
    • רשימה מקושרת עדיפה: גודל דינמי, הכנסה/מחיקה יעילה באמצע (O(1) לאחר מציאת המיקום). דוגמאות: תור, מחסנית, רשימת השמעה.
    • מערך עדיף: גישה ישירה לפי אינדקס (O(1)), ניצול זיכרון נמוך יותר (אין צורך בהפניות). דוגמאות: אוסף בגודל קבוע, גישה תכופה לאיברים לפי אינדקס.
  • השפעת Aliasing:
    • אם שתי הפניות מצביעות לאותו צומת, שינוי דרך אחת ישפיע על השנייה.
    • דוגמה: אם current.next = temp; ובטעות temp הוא צומת קיים ברשימה במקום צומת חדש, הדבר יכול ליצור לולאה אינסופית או לנתק חלקים מהרשימה.
  • יתרונות/חסרונות דו-כיוונית מול חד-כיוונית:
    • דו-כיוונית:
      • יתרונות: מעבר בשני הכיוונים, מחיקה יעילה יותר (O(1) לאחר מציאת הצומת) ללא צורך בצומת הקודם, הוספה בסוף ב-O(1) גם ללא tail (אם עוברים מה-tail).
      • חסרונות: דורשת יותר זיכרון (הפניה נוספת), מ
        מצאתם טעות או שחסר משהו?
→ הקודמת
אלגוריתמי מיון
הבאה ←
מחסנית ותור