Presentation is loading. Please wait.

Presentation is loading. Please wait.

מבוא כללי למדעי המחשב רשימות מקושרות www.cs.tau.ac.il/courses/cs4math/09b 3579.

Similar presentations


Presentation on theme: "מבוא כללי למדעי המחשב רשימות מקושרות www.cs.tau.ac.il/courses/cs4math/09b 3579."— Presentation transcript:

1 מבוא כללי למדעי המחשב רשימות מקושרות www.cs.tau.ac.il/courses/cs4math/09b 3579

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

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

4 דוגמא בציור: רשימה מקושרת של מספרים 3579 3579 לעומת מערך:

5 דוגמא בציור: הוספת איבר בהתחלה 7 359 35 9 7 (במימוש על-ידי מערך היינו צריכים להזיז קדימה את כל הערכים שלו, בהנחה שהיה מקום)

6 דוגמא בציור: הוספת איבר בסוף 35 9 7 7359

7 עוד דוגמא בציור: מחיקת איבר נניח שנתונה הרשימה הבאה של מספרים שלמים : נניח שנתונה הרשימה הבאה של מספרים שלמים : נניח שרוצים למחוק את האיבר שבו נמצא המספר 7. נניח שרוצים למחוק את האיבר שבו נמצא המספר 7. אז צריך למצוא את מי שמצביע עליו (5), ולגרום לו להצביע על מי שאחרי 7 (כלומר 9). אז צריך למצוא את מי שמצביע עליו (5), ולגרום לו להצביע על מי שאחרי 7 (כלומר 9). 3579

8 עוד דוגמא בציור: מחיקת איבר יוצרים קישור שעוקף את 7, ומוחקים את האיבר עם הערך 7. יוצרים קישור שעוקף את 7, ומוחקים את האיבר עם הערך 7. 3579 (במערכים היינו צריכים "לצופף" את כל איברי המערך)

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

10 תזכורת: מימוש רשימות מקושרות ב-C איבר ברשימה ייוצג על-ידי מבנה שיכיל לפחות שני שדות: לפחות שדה אחד יכיל מידע (בדוגמא שלנו יש שדה אחד שמכיל מספר שלם), ושדה אחד יכיל מצביע לאיבר הבא. איבר ברשימה ייוצג על-ידי מבנה שיכיל לפחות שני שדות: לפחות שדה אחד יכיל מידע (בדוגמא שלנו יש שדה אחד שמכיל מספר שלם), ושדה אחד יכיל מצביע לאיבר הבא. אז עבור הדוגמא של רשימת מספרים שלמים נגדיר איבר ע"י: אז עבור הדוגמא של רשימת מספרים שלמים נגדיר איבר ע"י: typedef struct Item { typedef struct Item { int data; int data; struct Item *next; struct Item *next; } Item; השדה data יכיל את המספר שהאיבר מכיל. השדה data יכיל את המספר שהאיבר מכיל. השדה next יכיל מצביע לאיבר הבא ברשימה. (אם אין איבר נוסף, כלומר זה האיבר האחרון, אז ערך המצביע הזה יהיה NULL) השדה next יכיל מצביע לאיבר הבא ברשימה. (אם אין איבר נוסף, כלומר זה האיבר האחרון, אז ערך המצביע הזה יהיה NULL) 3 datanext

11 בניית רשימה חדשה: הוספת איברים מעצם טבעה של רשימה מקושרת (שיש בה תוספות ומחיקות), מספר האיברים בה לא ידוע מראש ואין עליו חסם. אנחנו נדרשים להוסיף איברים במהלך ריצת התוכנית. מעצם טבעה של רשימה מקושרת (שיש בה תוספות ומחיקות), מספר האיברים בה לא ידוע מראש ואין עליו חסם. אנחנו נדרשים להוסיף איברים במהלך ריצת התוכנית. תוספת איבר לרשימה מקושרת תיעשה בעזרת הקצאה דינאמית שלו. הקצאה דינאמית של איבר אחד נעשית במקרה זה ע"י: תוספת איבר לרשימה מקושרת תיעשה בעזרת הקצאה דינאמית שלו. הקצאה דינאמית של איבר אחד נעשית במקרה זה ע"י: Item *ptr; ptr = (Item *) malloc (sizeof(Item)); נתאר פונקציה להוספת איבר כזה לרשימה. נתאר פונקציה להוספת איבר כזה לרשימה.

12 הוספת איבר לתחילת רשימה  אמרנו שתמיד נשמור מצביע לאיבר הראשון ברשימה, וממנו נוכל להתקדם לאחרים.  נממש פונקציה בשם Add, שתקבל מצביע לראש הרשימה וערך להוספה, ותוסיף את הערך בתחילת הרשימה.  הפונקציה תחזיר מצביע לראש הרשימה (החדש).  (נוכל להשתמש בפונקציה הזאת לבניית רשימה מקושרת חדשה אם נתחיל עם רשימה ריקה, כלומר עם מצביע שערכו NULL).

13 רשימות מקושרות-הוספת איבר בהתחלה 7 359 35 9 7

14 Add מימוש פונקציית ההוספה פונקציית ההוספה צריכה לעשות שלושה דברים: פונקציית ההוספה צריכה לעשות שלושה דברים: ליצור איבר עבור המידע החדש. ליצור איבר עבור המידע החדש. לדאוג שהוא יצביע על תחילת הרשימה הקיימת (או על NULL אם היא ריקה). לדאוג שהוא יצביע על תחילת הרשימה הקיימת (או על NULL אם היא ריקה). להחזיר מצביע על האיבר החדש (תחילת הרשימה החדשה). להחזיר מצביע על האיבר החדש (תחילת הרשימה החדשה). האיבר החדש צריך להיות מוקצה דינאמית ע"י הפונקציה (משתנה מקומי רגיל נעלם בסוף הפונקציה).

15 פונקציה להוספת איבר בהתחלה Item* Add(Item* head, int value) { Item* new_item; Item* new_item; new_item = (Item*) malloc(sizeof (Item)); new_item = (Item*) malloc(sizeof (Item)); new_item->data = value; new_item->data = value; new_item->next = head; new_item->next = head; return new_item; return new_item;} האיבר החדש יצביע לראש הרשימה הקודם, ויהיה ראש הרשימה החדש. יוצרים איבר חדש עם הערך הדרוש (לצורך ההדגמה הנחנו שהקצאת הזיכרון הצליחה) מקבלים מצביע לראש רשימה וערך להוספה

16 head head 3 רשימות מקושרות ב-C – שימוש ב- Add …. int main() { Item *head = NULL; head = Add(head,7); head = Add(head,3); head = Add(head,5);....} 7head 5 head מתחילים מרשימה ריקה וכל פעם מוסיפים לה ערך בהתחלה

17 מציאת איבר ברשימה פעולה נפוצה נוספת שנירצה לממש היא חיפוש איבר ברשימה והחזרת מצביע אליו. פעולה נפוצה נוספת שנירצה לממש היא חיפוש איבר ברשימה והחזרת מצביע אליו. הפרמטרים לפונקציה יהיו: הפרמטרים לפונקציה יהיו: מצביע לראש הרשימה מצביע לראש הרשימה הערך שאותו מחפשים הערך שאותו מחפשים הערך המוחזר יהיה: הערך המוחזר יהיה: מצביע לאיבר עם הערך המבוקש (NULL אם האיבר אינו ברשימה) מצביע לאיבר עם הערך המבוקש (NULL אם האיבר אינו ברשימה)

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

19 מציאת איבר ברשימה Item *Find(Item *head, int value) { Item *check = head; while ((check != NULL) && (check->data != value)) check = check->next; return check; } מתחילים לחפש מראש הרשימה. כל עוד לא הגענו לסוף ולא מצאנו את הערך המבוקש – מתקדמים ברשימה. הפונקציה מקבלת מצביע לראש הרשימה וערך שרוצים למצוא בה כשהגענו לערך המבוקש או לסוף, מחזירים מצביע למקום שהגענו אליו (באופן דומה יכולנו לחפש את האיבר הראשון שמקיים תנאי כלשהו)

20 רשימות מקושרות-מציאת איבר 7 359 למשל אם נחפש את 5: head check

21 רשימות מקושרות-מציאת איבר 7 359 למשל אם נחפש את 5: head check

22 רשימות מקושרות-מציאת איבר 7 359 למשל אם נחפש את 5: head check

23 דוגמא: מציאת איבר ברשימה Item *Find(Item *head, int value) { Item *check = head; while ((check != NULL) && (check->data != value)) check = check->next; return check; } מתחילים לחפש מראש הרשימה. כל עוד לא הגענו לסוף ולא מצאנו את הערך המבוקש – מתקדמים ברשימה. הפונקציה מקבלת מצביע לראש הרשימה וערך שרוצים למצוא בה כשהגענו לערך המבוקש או לסוף, מחזירים מצביע למקום שהגענו אליו אם value לא נמצא ברשימה, אז בפעם האחרונה שהלולאה תתבצע יתקיים ש- check הוא NULL, אבל לא יבדק הערך של check->data, כך שלא תהיה בעיה (לא ניגש לכתובת NULL). אם value לא נמצא ברשימה, אז בפעם האחרונה שהלולאה תתבצע יתקיים ש- check הוא NULL, אבל לא יבדק הערך של check->data, כך שלא תהיה בעיה (לא ניגש לכתובת NULL).

24 שחרור רשימה מקושרת בסיום השימוש ברשימה נרצה לשחרר את כל הזיכרון שהיא משתמשת בו. בסיום השימוש ברשימה נרצה לשחרר את כל הזיכרון שהיא משתמשת בו. - בשונה ממערך שהוקצה דינאמית, free של ראש הרשימה משחרר רק את האיבר הראשון, כי כל אחד מאיברי הרשימה הוקצה ע"י malloc נפרד, ו- free משחרר את מה שהוקצה בפקודת malloc אחת בדיוק. נכתוב פונקציה שמקבלת מצביע לראש הרשימה ועוברת ומשחררת את כל האיברים שהוקצו על ידי פונקצית ההוספה. נכתוב פונקציה שמקבלת מצביע לראש הרשימה ועוברת ומשחררת את כל האיברים שהוקצו על ידי פונקצית ההוספה.

25 שחרור רשימה מקושרת - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head כל פעם משחררים את האיבר הראשון ברשימה שנשארה, עד שכולה משוחררת. ראש הרשימה מתקדם לאיבר הבא. מתחילים לשחרר מראש הרשימה. משחררים את ראש הרשימה הקודם. באיטרציה הבאה ישוחרר ראש הרשימה הבא. to_free מקבלים מצביע לראש רשימה מקושרת

26 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head כל פעם מוחקים את האיבר הראשון שנשאר ברשימה, עד שהיא מתרוקנת.

27 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head מצביע נוסף על האיבר הראשון, שישוחרר to_free

28 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head ממשיכים כל עוד לא הגענו לסוף הרשימה to_free מצביע נוסף על האיבר הראשון, שישוחרר

29 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

30 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

31 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free כעת ישוחרר האיבר הבא האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

32 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free כעת ישוחרר האיבר הבא האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

33 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free כעת ישוחרר האיבר הבא האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

34 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free כעת ישוחרר האיבר הבא האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

35 שחרור רשימה - מימוש void FreeList (Item* head) { Item* to_free = head; while (to_free != NULL) { head = head->next; free(to_free); to_free = head; }} head to_free כשהגענו ל- NULL מסיימים כעת ישוחרר האיבר הבא האיבר הראשון משוחרר ראש הרשימה מתקדם ממשיכים כל עוד לא הגענו לסוף הרשימה מצביע נוסף על האיבר הראשון, שישוחרר

36 רשימות מקושרות – עוד דוגמאות דוגמאות נוספות לפעולות נפוצות על רשימות מקושרות:  הוספה בסוף רשימה  מחיקה מאמצע רשימה

37 הוספת איבר בסוף 35 9 7 7 35 9

38 הוספת איבר בסוף - השלבים נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. 35 9 7

39 הוספת איבר בסוף - השלבים נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. 35 9 7

40 הוספת איבר בסוף - השלבים נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. 35 9 7

41 הוספת איבר בסוף - השלבים נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. 35 9 7

42 הוספת איבר בסוף - השלבים נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. נחפש את האיבר האחרון ברשימה, וכאשר נמצא אותו נגרום לו להצביע על 7, ו-7 יהפוך להיות האיבר האחרון. 3 597

43 מחיקת איבר מאמצע הרשימה 35 77 35 9

44 נחפש את האיבר שלפני האיבר שמוצבע אליו ברשימה, וכאשר נמצא אותו נגרום לו להצביע על האיבר שאחרי האיבר שמוצבע אליו. נחפש את האיבר שלפני האיבר שמוצבע אליו ברשימה, וכאשר נמצא אותו נגרום לו להצביע על האיבר שאחרי האיבר שמוצבע אליו. 7 35 9


Download ppt "מבוא כללי למדעי המחשב רשימות מקושרות www.cs.tau.ac.il/courses/cs4math/09b 3579."

Similar presentations


Ads by Google