Menu
Coddy logo textTech

Linked List (רשימה מקושרת)

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

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

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

סיבוכיות זמן

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

רשימה מקושרת מול מערך

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

דוגמה מפורטת

בניית הרשימה [10, 20], הוספת 5 בהתחלה, ואז מחיקת 20:

צעדמבנהפעולה
התחלהhead -> nullרשימה ריקה
הכנסה לראש 10head -> 10 -> nullמפנים את הראש לצומת חדש שה-next שלו הוא הראש הישן (null)
הכנסה לזנב 20head -> 10 -> 20 -> nullהולכים לצומת 10 וקובעים את ה-next שלו לצומת חדש 20
הכנסה לראש 5head -> 5 -> 10 -> 20 -> nullהצומת החדש 5 מצביע לראש הישן 10; הראש מצביע עכשיו ל-5
מחיקת 20head -> 5 -> 10 -> nullהולכים ל-10 ומשנים את ה-next שלו מ-20 ל-null; הצומת 20 מנותק

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

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

קוד Linked List

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

קוד Linked List ב-Python

Python
1class Node:2    def __init__(self, value):3        self.value = value4        self.next = None5
6
7class LinkedList:8    def __init__(self):9        self.head = None10
11    def append(self, value):12        node = Node(value)13        if self.head is None:14            self.head = node15            return16        current = self.head17        while current.next:18            current = current.next19        current.next = node20
21    def find(self, value):22        current = self.head23        while current:24            if current.value == value:25                return True26            current = current.next27        return False28
29    def delete(self, value):30        # Re-link the previous node around the match31        current, prev = self.head, None32        while current:33            if current.value == value:34                if prev is None:35                    self.head = current.next36                else:37                    prev.next = current.next38                return True39            prev, current = current, current.next40        return False41
42    def __str__(self):43        values, current = [], self.head44        while current:45            values.append(str(current.value))46            current = current.next47        return " -> ".join(values) + " -> None"48
49
50lst = LinkedList()51for value in [3, 7, 1, 9]:52    lst.append(value)53
54print("List:    ", lst)55print("find(7): ", lst.find(7))56lst.delete(1)57print("After delete(1):", lst)
להריץ את הקוד הזה בעורך ה-Python אונליין

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

מה ההבדל בין רשימה מקושרת למערך?
מערך שומר איברים בבלוק רציף אחד, מה שנותן גישה אקראית של O(1) אבל הכנסות ומחיקות של O(n) שמזיזות איברים. רשימה מקושרת שומרת צמתים בכל מקום בזיכרון, מחוברים במצביעים, מה שנותן הכנסות ומחיקות של O(1) במיקום ידוע אבל גישה של O(n), כי צריך ללכת לאורך השרשרת.
מתי כדאי להשתמש ברשימה מקושרת?
רשימות מקושרות מצטיינות כשמכניסים או מסירים לעתים קרובות בקצוות (או בצומת שכבר יש אליו הפניה) וכמעט לא צריכים גישה אקראית: למשל תורים, מחסניות, רשימות סמיכות ומטמוני LRU. אם צריך גישה מהירה לפי אינדקס, מערך או מערך דינמי בדרך כלל עדיפים.
מהי סיבוכיות הזמן של רשימה מקושרת?
הכנסה או מחיקה בראש הן O(1). חיפוש, או הגעה לזנב בלי מצביע זנב, הם O(n), כי הולכים לפי מצביעי next אחד אחד. אין גישה אקראית של O(1): גישה לפי אינדקס ברשימה מקושרת היא O(n).
מה ההבדל בין רשימה מקושרת חד כיוונית לדו כיוונית?
רשימה מקושרת חד כיוונית שומרת רק מצביע next לכל צומת, כך שאפשר ללכת בכיוון אחד, ומחיקת צומת דורשת הפניה לצומת שלפניו. רשימה מקושרת דו כיוונית מוסיפה מצביע prev, שמאפשר מעבר לאחור ומחיקה של O(1) של צומת שבידיכם, במחיר של מצביע נוסף לכל צומת ויותר ניהול בכל הכנסה ומחיקה.
למה הכנסה בזנב היא O(n) אם הכנסה בראש היא O(1)?
הכנסה בראש רק מחברת מחדש את מצביע ה-head, וזו עבודה קבועה. הגעה לזנב פירושה הליכה לפי מצביעי next מהראש עד הסוף, וזה O(n). שמירת מצביע tail נפרד הופכת גם את ההכנסה בזנב ל-O(1), ולכן רשימות בעולם האמיתי עוקבות לעתים קרובות אחרי שני הקצוות.
האם לרשימות מקושרות יש הכנסה של O(1) בכל מקום?
לא, וזו תפיסה שגויה נפוצה. ההכנסה עצמה היא O(1) רק כשכבר יש בידיכם מצביע לצומת שאחריו מכניסים. מציאת המיקום הזה לפי ערך או אינדקס עדיין עולה O(n), כי צריך ללכת לאורך השרשרת כדי להגיע אליו.
איור של שפות התכנות ב-Coddy

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

להתחיל