מבני נתונים רשימה מקושרת, מחסנית ותור

Slides:



Advertisements
Similar presentations
ממיבחניםC שאלות ++.
Advertisements

מבוא למדעי המחשב לתעשייה וניהול
טבלאות סמלים נכתב ע"י אלכס קוגן סמסטר חורף, תשס"ח.
1 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
תכנות תרגול 11 שבוע : מבנים מטרת המבנים היא לאפשר למתכנת להגדיר טיפוסי משתנים חדשים אשר מתאימים ספציפית לבעיה שאותה התוכנית פותרת. מטרת המבנים.
תכנות תרגול 6 שבוע : חישוב e זוהי הנוסחא לחישוב e נראה כיצד לתרגם אותה לפונקציה n n.
תכנות תרגול 4 שבוע : לולאות while לולאות while while (condition) { loop body } במקרה של קיום התנאי מתבצע גוף הלולאה ברגע שהתנאי לא מתקיים נצא.
מבוא לשפת C חידות ונקודות חשובות נכתב על-ידי יורי פקלני. © כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל.
מבוא למדעי המחשב תרגול 8 - מחרוזות שעת קבלה : יום שני 11:00-12:00 דוא " ל :
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
תכנות תרגול 6 שבוע : תרגיל שורש של מספר מחושב לפי הסדרה הבאה : root 0 = 1 root n = root n-1 + a / root n-1 2 כאשר האיבר ה n של הסדרה הוא קירוב.
11 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
תכנות תרגול 6 שבוע : הגדרת פונקציות return-value-type function-name(parameter1, parameter2, …) הגדרת סוג הערכים שהפונקציה מחזירה שם הפונקציהרשימת.
תכנות תרגול 14 שבוע:
תכנות תרגול 14 שבוע : רשימות מקושרות ישנו מבנה נתונים אשר מאפשר ישנו מבנה נתונים אשר מאפשר לנו לבצע את הוספת האיברים בצורה נוחה יותר. מבנה זה.
מבוא כללי למדעי המחשב תרגול 3. לולאות while לולאות while while (condition) { loop body } במקרה של קיום התנאי מתבצע גוף הלולאה ברגע שהתנאי לא מתקיים נצא.
מחסנית (Stack): מבנה נתונים עליו מוגדרות הפעולות הבאות:
קורס תכנות – סימסטר ב ' תשס " ח שיעור שישי: מערכים
תכנות תרגול 12 שבוע : הקצאת זיכרון דינאמית הזיכרון המקסימאלי ששימש את התוכנית שלנו עד היום היה קבוע מראש. לפני הרצת התוכנית, לאחר שהתוכנית עברה.
מבוא כללי למדעי המחשב רשימות מקושרות
מבוא למדעי המחשב תרגול 6 - מערכים שעת קבלה : יום שני 11:00-12:00 דוא " ל :
תכנות תרגול 5 שבוע : הגדרת פונקציות return-value-type function-name(parameter1, parameter2, …) הגדרת סוג הערכים שהפונקציה מחזירה שם הפונקציהרשימת.
Stacks and Queues. Stack ADT סוג של מערך מוגבל מהר מאוד ולוקחים מעט זכרון שימוש ב LIFO – LIFO (Last In, First Out) lists. –אפשר להוסיף רק בסוף הרשימה.
מערכים עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר עד היום כדי לייצג 20 סטודנטים נאלצנו להגדיר int grade1, grade2, …, grade20; int grade1, grade2, …, grade20;
עקרון ההכלה וההדחה.
תכנות מונחה עצמים Object Oriented Programming (OOP) אתגר מחזור ב' Templates תבניות.
תרגול 12: ומחסניתIteratorרשימה מקושרת, תור, 1. רשימה מקושרת (Linked List) רשימה מקושרת הינה קבוצה סדורה של אובייקטים, כאשר כל אובייקט ברשימה מכיל הצבעה.
1 מבוא למדעי המחשב סיבוכיות. 2 סיבוכיות - מוטיבציה סידרת פיבונאצ'י: long fibonacci (int n) { if (n == 1 || n == 2) return 1; else return (fibonacci(n-1)
ומחסניתIteratorרשימה מקושרת, תור,
תכנות תרגול 12 שבוע : מבנים מטרת המבנים היא לאפשר למתכנת להגדיר טיפוסי משתנים חדשים אשר מתאימים ספציפית לבעיה שאותה התוכנית פותרת. מטרת המבנים.
Structure. מה לומדים היום ? דרך לבנות מבנה נתונים בסיסי – Structure מייצר " טיפוס " חדש מתאים כאשר רוצים לאגד כמה משתנים יחד דוגמאות : עובד : שם, טלפון,
בתרגול הקודם כללי הרשאות (Visibility modifiers) בהורשה – Public – Protected – private חוקי גישה לשדות ושיטות בהורשה ב -Java מחלקות אבסטרקטיות –המילה השמורה.
1 מבוא למדעי המחשב הרצאה 21: Queue, Iterator & Iterable.
תרגול 12: Iterator מחסנית תור 1. מחסנית (stack) מחסנית (stack) היא מבנה נתונים שמזכיר מחסנית של רובה : האיבר שנכנס ראשון למחסנית יוצא ממנה אחרון (LIFO.
מחסנית ותור Stacks and Queues. מחסנית Stack מחסנית - Stack ADT סוג של מערך מוגבל מהיר מאוד ותופס מעט זיכרון שימוש ב LIFO – LIFO (Last In, First Out)
Void*, pointer to functions, variadic functions קרן כליף.
Practice session 6 Sequence Operations Partial Evaluation Lazy Lists.
מבוא למדעי המחשב לתעשייה וניהול הרצאה 12. ספריות.
סיביות קרן כליף. © Keren Kalif 2 ביחידה זו נלמד:  מוטיבציה  אופרטורים לעבודה עם סיביות:  &  |  ^  ~  >> 
מחסנית ותור Stacks and Queues. מחסנית Stack מחסנית - Stack ADT סוג של מערך מוגבל מהיר מאוד ותופס מעט זיכרון שימוש ב LIFO – LIFO (Last In, First Out)
מבנים קרן כליף. ביחידה זו נלמד :  מהו מבנה (struct)  איתחול מבנה  השמת מבנים  השוואת מבנים  העברת מבנה לפונקציה  מבנה בתוך מבנה  מערך של מבנים.
מבנה נתונים או טיפוס נתונים מופשט חלק ב – פעולות על רשימה הוכן ע " י ולרי פקר דצמבר 2015.
1 Introduction to Programming in C - Fall 2010 – Erez Sharvit, Amir Menczel 1 Introduction to Programming in C תרגול
מספרים אקראיים ניתן לייצר מספרים אקראיים ע"י הפונקציה int rand(void);
Programming Arrays.
תכנות מכוון עצמים ו- C++ יחידה 11 תבניות - templates
הרצאה 10 פונקציות עם מספר משתנה של פרמטרים
מחלקות classes.
מבוא למדעי המחשב סיבוכיות.
הקצאות דינאמיות בשילוב מבנים
מבנה נתונים ואלגוריתמים
מיונים וחיפושים קרן כליף.
מצביעים קרן כליף.
מערכים קרן כליף.
תירגול 14: מבני נתונים דינאמיים
הרצאה 06 רשימות מקושרות קרן כליף.
תכנות מכוון עצמים ושפת JAVA
הקצאות דינאמיות קרן כליף.
מבני נתונים עצים קרן כליף.
מבנים קרן כליף.
מצביעים קרן כליף.
Engineering Programming A
הרצאה 21: Queue, Iterator & Iterable
בניית מחסנית סטטית Static Stack Implementation מורים מובילים תשע"ה
שאלה 1.
מבוא כללי למדעי המחשב תרגול 4
מחסנית ותור Stacks and Queues.
Computer Programming תרגול 3 Summer 2016
Engineering Programming A
מבוא לתכנות ב- Java תרגול 10 - רשימות מקושרות.
Presentation transcript:

מבני נתונים רשימה מקושרת, מחסנית ותור קרן כליף

ביחידה זו נלמד: מהי רשימה מקושרת פונקציות של רשימות מקושרות מימושים רקורסיביים לפונקציות של רשימות מקושרות ההבדל בין מערך לרשימה וריאציות של רשימות מקושרות תור ומחסנית

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

רשימה מקושרת זהו מבנה נתונים המאפשר הוספה דינאמית של איברים לכל מקום באוסף בקלות אין צורך להחליט מראש כמה איברים יהיו ברשימה הרעיון הוא יצירת איבר וקישורו למקום המתאים ברשימה בכל רגע נתון כל איבר ברשימה מקושרת הוא מבנה המכיל נתון והצבעה לאיבר הבא האיבר האחרון ברשימה יצביע ל- NULL בניגוד למערך, האיברים אינם ברצף בזיכרון typedef int type; typedef struct LNode } type data; struct LNode* next; } LNode; 4 9 6 typedef struct List } struct LNode* head; } List;

דוגמת הוספת ערכים לרשימה lst head 1 ??? 2 2 void main() { List lst; lst.head = (LNode*)calloc(1, sizeof(LNode)); lst.head->data = 1; lst.head->next = (LNode*)calloc(1, sizeof(LNode)); lst.head->next->data = 2; }

פונקציה המחשבת את מספר הצמתים ברשימה int getListLength(const List* lst) { int count = 0; LNode* temp = lst->head; while (temp != NULL) count++; temp = temp->next; } return count; count = 0 count = 1 count = 2 temp lst head 1 ??? 2 2

פונקציה המדפיסה את איברי הרשימה void printList(const List* lst) { LNode* temp = lst->head; while (temp != NULL) printf("%d ", temp->data); temp = temp->next; } 1 2 1 temp lst head 1 ??? 2 2

פונקציה המשחררת את איברי הרשימה void freeList(List* theList) { LNode* current = theList->head; LNode* next; while (current) next = current->next; free(current); current = next; } theList->head = NULL; current next lst 1 head ??? 2 2

שדרוג הרשימה עד כה החזקנו בתוך המבנה List מצביע לראש הרשימה, והוספת איבריו הייתה באמצעותו: void main() { List lst; lst.head = (LNode*)calloc(1, sizeof(LNode)); lst.head->data = 1; lst.head->next = (LNode*)calloc(1, sizeof(LNode)); lst.head->next->data = 2; lst.head->next->next = (LNode*)calloc(1, sizeof(LNode)); lst.head->next->next->data = 3; freeList(&lst); }

שדרוג הרשימה כדי להקל על הוספת איברים לסוף הרשימה נשדרג את המבנה List כך שיכיל גם מצביע לאיבר האחרון: void main() { List lst; lst.head = (LNode*)calloc(1, sizeof(LNode)); lst.head->data = 1; lst.tail = lst.head; lst.tail->next = (LNode*)calloc(1, sizeof(LNode)); lst.tail->next->data = 2; lst.tail = lst.tail->next; lst.tail->next->data = 3; freeList(&lst); } typedef struct List } struct LNode *head, *tail; } List; ניתן לראות שכעת הקוד של הוספת איבר לסוף הרשימה זהה, ובפרט חוסך לולאה המטיילת לאיבר האחרון, ולכן יעילות הפעולה היא (1)O lst 3 1 head 2 tail

פונקציה המאתחלת רשימה ריקה פונקציה זו מאתחלת בשדות ה- head וה- tail את הערך NULL List makeEmptyList() { List lst; lst.head = lst.tail = NULL; return lst; }

פונקציה הבודקת האם רשימה ריקה int isEmpty(const List* lst) { return lst->head == NULL; }

פונקציה שיוצרת איבר להכנסה לרשימה הפונקציה תקבל את הערך שיהיה באיבר, ומצביע לאיבר הבא: פונקציה זו תשמש אותנו בפונקציות הבאות LNode* createNewNode(type newData, LNode* next) { LNode* newNode = (LNode*)calloc(1, sizeof(LNode)); newNode->data = newData; newNode->next = next; return newNode; }

הוספת ערך לראש הרשימה void insertValueToHead(List* lst, type newData) { LNode* newNode = createNewNode(newData, lst->head); if (isEmpty(lst)) lst->head = lst->tail = newNode; else lst->head = newNode; } 3 newData = 4 2 lst head tail 4 newNode

הוספת ערך לראש הרשימה (שהפעם ריקה) void insertValueToHead(List* lst, type newData) { LNode* newNode = createNewNode(newData, lst->head); if (isEmpty(lst)) lst->head = lst->tail = newNode; else lst->head = newNode; } newData = 4 lst head tail 4 newNode

הוספת ערך לסוף הרשימה void insertValueToTail(List* lst, type newData) { LNode* newNode = createNewNode(newData, NULL); if (isEmpty(lst)) lst->head = lst->tail = newNode; else lst->tail->next = newNode; lst->tail = newNode; } 3 2 lst newData = 4 head tail 4 newNode

הוספת ערך לסוף הרשימה (שהפעם ריקה) void insertValueToTail(List* lst, type newData) { LNode* newNode = createNewNode(newData, NULL); if (isEmpty(lst)) lst->head = lst->tail = newNode; else lst->tail->next = newNode; lst->tail = newNode; } lst newData = 4 head tail 4 newNode

הוספת איבר לסוף הרשימה void insertNodeToTail(List* lst, LNode* newNode) { if (isEmpty(lst)) lst->head = lst->tail = newNode; else lst->tail->next = newNode; lst->tail = newNode; }

דוגמאת החברים

דוגמא: פיצול רשימה לזוגיים ואי-זוגיים void main() { List lst = makeEmptyList(); List lstEven, lstOdd; int i; for (i=1 ; i <= 10 ; i++) insertValueToTail(&lst, i); splitListToEvenAndOdd(&lst, &lstEven, &lstOdd); printf("There are %d even nodes: ", getListLength(&lstEven)); printList(&lstEven); printf("\n"); printf("There are %d odd nodes: ", getListLength(&lstOdd)); printList(&lstOdd); freeList(&lstEven); freeList(&lstOdd); }

דוגמא: פיצול רשימה לזוגיים ואי-זוגיים (2) דוגמא: פיצול רשימה לזוגיים ואי-זוגיים (2) void splitListToEvenAndOdd(List* src, List* even, List* odd) { LNode* current = src->head; LNode* next; *even = makeEmptyList(); *odd = makeEmptyList(); while (current) next = current->next; if (current->data%2 == 0) insertNodeToTail(even, current); else insertNodeToTail(odd, current); current->next = NULL; current = next; } 3 1 2 src head tail even head tail current next odd head tail פונקציה זו הורסת את הרשימה המקורית. כדי לשמור עליה היה צריך להעתיק קודם את רשימת המקור.

העתקת רשימה List copyList(const List* lst) { List res = makeEmptyList(); LNode* temp = lst->head; while (temp) insertValueToTail(&res, temp->data); temp = temp->next; } return res;

הרעיון מאחורי הוספת ערך לאמצע רשימה דוגמה: הוספת הערך 8 אחרי הערך 9 ראשית נייצר איבר חדש נחבר את האיבר החדש לאיבר שאמור להיות אחריו נחבר לאיבר החדש את האיבר שלפניו 4 9 6 8

הוספת איבר לאמצע הרשימה (אחרי ערך מסוים) void insertAfterValue(List* lst, type insertAfter, type newValue) { LNode* temp = lst->head; while (temp && temp->data != insertAfter) temp = temp->next; if (temp) LNode* newNode = createNewNode(newValue, temp->next); temp->next = newNode; הוספה לאחר איבר שלא קיים - לא תבוצע 2 9 6 insertAfter = 9 newValue = 8 head temp temp 8 newNode

5 2 9 מחיקת איבר void removeFirstValueOf(List* lst, type toRemove) { LNode* prev = lst->head; LNode* temp = lst->head; while (temp && temp->data != toRemove) prev = temp; temp = temp->next; prev->next = temp->next; free(temp); 5 toRemove = 9 prev 2 9 head temp temp

2 9 6 חיפוש ערך LNode* findValue(List* lst, type lookFor) { LNode* temp = lst->head; while (temp) if (temp->data == lookFor) return temp; temp = temp->next; return NULL; } lookFor = 9 2 9 6 head temp temp

2 4 1 7 2 דוגמא true false true void main() { List list = makeEmptyList(); LNode* found; printf("Is Empty? %s\n", isEmpty(&list) ? "true" : "false"); insertValueToHead(&list, 4); insertValueToHead(&list, 7); insertValueToTail(&list, 1); insertAfterValue(&list, 4, 2); insertAfterValue(&list, 8, 9); // doesn't add.. insertAfterValue(&list, 1, 2); removeFirstValueOf(&list, 2); found = findValue(&list, 4); found = findValue(&list, 10); printList(&list); printf(“\nIs Empty? %s\n", isEmpty(&list) ? "true" : "false"); freeList(&list); 2 true 4 head 1 7 2 false true

מימושים רקורסיביים – שחרור רשימה void freeListRec(List* lst) { freeListHelper(lst->head); lst->head = NULL; lst->tail = NULL; } void freeListHelper(LNode* head) { if (head == NULL) return; freeListHelper(head->next); free(head); }

מימושים רקורסיביים – הדפסת רשימה void printListRec(List* lst) { printListHelper(lst->head); } void printListHelper(LNode* head) { if (head == NULL) printf("\n"); return; } printf("%d ", head->data); printListHelper(head->next);

מימושים רקורסיביים – חיפוש איבר LNode* findValueRec(List* lst, type lookFor) { return findValueRec(lst->head, lookFor); } LNode* findValueHelper(LNode* head, type lookFor) { if (head == NULL) return NULL; if (head->data == lookFor) return head; return findValueRec(head->next, lookFor); }

מימושים רקורסיביים – אורך רשימה int getListLengthRec(List* lst) { return getListLengthHelper(lst->head); } int getListLengthHelper(LNode* head) { if (head == NULL) return 0; return 1 + getListLengthHelper(head->next); }

דוגמאת ניהול הזכרון

וריאציות של רשימה מקושרת עבודה עם dummy head: בעת הוספה/הסרה וכד' חוסך בדיקה האם האיבר הוא הראשון רשימה דו-כיוונית: יעיל עבור טיול דו כיווני, מאפשר לא לשמור את המשתנה prev בכל מקום בו צריך לשמור הפניה לאיבר הקודם (למשל במחיקה) מאפשר גישה מיידית לאיבר קודם מקל על פעולת החלפה של 2 איברים רשימה מעגלית האיבר האחרון מצביע לראשון

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

רשימה מקושרת כללית פונקציות ההוספה / הסרה / הדפסה וכו' יכולות להיות כלליות ואין צורך שיהיו מוגבלות עבור טיפוס מסויים בלבד נבצע את השינוי הבא בהגדרת המבנה Node: נעדכן את המבנה List: typedef struct Node { void* data; struct Node* next; } LNode; typedef struct { struct LNode *head, *tail; void (*printNode)(void*); int (*compare)(void*, void*); } List;

רשימה מקושרת כללית – הקוד (1) List makeEmptyList(void (*printNode)(void*), int (*compare)(void*, void*)) } List theList = { {NULL, NULL}, printNode, compare}; return theList ; { void insertValueToHead(List* theList , void* newValue) } LNode* newNode = (LNode*)calloc(1, sizeof(LNode)); newNode->data = newValue; if (isEmpty(theList )) theList ->head = theList ->tail = newNode; else { newNode->next = theList->head; theList->head = newNode;

רשימה מקושרת כללית – הקוד (2) void insertValueToTail(List* theList , void* newValue) } LNode* newNode = (LNode*)calloc(1, sizeof(LNode)); newNode->data = newValue; if (isEmpty(theList )) theList>head = theList->tail = newNode; else { theList->theList->next = newNode; theList->tail = newNode; void printList(List* theList) { LNode* temp = theList->head; while (temp) theList->printNode(temp->data); temp = temp->next; printf("\n");

רשימה מקושרת כללית – הקוד (3) LNode* findValue(List* theList, void* lookFor) { LNode* temp = theList->head; while (temp) if (theList->compare(temp->data, lookFor) == 0) return temp; temp = temp->next; return NULL;

רשימה מקושרת כללית – דוגמאת שימוש void printStr(void* str) { printf("%s ", (char*)str); int compareStrings(void* str1, void* str2) } return strcmp((char*)str1, (char*)str2); { void main() { List list = makeEmptyList(printStr, compareStrings); char *str1="hi", *str2="bye", *str3="happy", *str4="birthday"; printf("Is Empty? %s\n", isEmpty(&list) ? "true" : "false"); insertValueToHead(&list, str1); insertValueToTail(&list, str2); insertValueToHead (&list, str3); insertValueToHead (&list, str4); printList(&list); freeList(&list);

תור תור הוא מבנה נתונים הפועל עפ"י העקרון של First In First Out הפעולות שניתן לבצע על תור: בדיקה האם התור ריק: isEmpty הוספה לסוף התור: enqueue הסרה מראש התור: dequeue המימוש יהיה באמצעות רשימה מקושרת: הוספה תמיד תתבצע לסוף הסרה תמיד תבוצע מהראש

דוגמא לשימוש בתור void enqueue(List* l, void* newValue) } insertToEnd(l, newValue); { void* dequeue(List* l) { return removeHead(l); void main() { List list = makeEmpty(printStr, compareStrings); char *str1="hi", *str2="bye", *str3="happy", *str4="birthday"; char* res; printf("Is Empty? %s\n", isEmpty(&list) ? "true" : "false"); enqueue(&list, str1); enqueue(&list, str2); enqueue(&list, str3); printList(&list); res = dequeue(&list); freeList(&list);

מחסנית מחסנית היא מבנה נתונים הפועל עפ"י העקרון של Lifo In First Out הפעולות שניתן לבצע על מחסנית: בדיקה האם המחסנית ריקה: isEmpty הוספה לראש המחסנית: push הסרה ראש המחסנית: pop קבלת הערך שבראש המחסנית: top עבור מחסנית שאינה מוגבלת בגדולה המימוש יהיה באמצעות רשימה מקושרת: הוספה תמיד תתבצע לראש הסרה תמיד תבוצע מהראש ניתן לממש מחסנית בגודל מוגבל ואז המימוש יוכל להיות באמצעות מערך. היתרון: חסכון בהקצאות הדינאמיות שיש ברשימה מקושרת

מימוש מחסנית ע"י מערך (1) #include <stdio.h> מימוש מחסנית ע"י מערך (1) #include <stdio.h> #include <stdlib.h> typedef struct { int* arr; int phisicalSize; int logicalSize; } Stack; Stack makeStack(int maxElements) } Stack s = {NULL, maxElements, 0}; s.arr = (int*)calloc(maxElements, sizeof(int)); return s; void freeStack(Stack* s) free(s->arr); s->logicalSize = 0; s->phisicalSize = 0;

מימוש מחסנית ע"י מערך (2) int isFull(Stack* s) int isEmpty(Stack* s) { return s->logicalSize == s->phisicalSize; void push(Stack* s, int newValue) if (!isFull(s)) s->arr[s->logicalSize++] = newValue; int top(Stack* s) } return s->arr[s->logicalSize-1]; // assumption: at lease one value is in the stack int pop(Stack* s) return s->arr[--(s->logicalSize)]; int isEmpty(Stack* s) { return s->logicalSize == 0;

מימוש מחסנית ע"י מערך (3) void main() { Stack s = makeStack(10); int topValue; push(&s, 5); push(&s, 2); push(&s, 7); push(&s, 8); push(&s, 1); printf("The stack is: "); printStack(&s); topValue = top(&s); printf("The stack after top: "); topValue = pop(&s); freeStack(&s); void printStack(Stack* s) { int i; printf("%d values (out of max %d):\n", s->logicalSize, s->phisicalSize); for (i=s->logicalSize -1 ; i >= 0; i--) printf("%d ", s->arr[i]); printf("\n");

ביחידה זו למדנו: מהי רשימה מקושרת פונקציות של רשימות מקושרות מימושים רקורסיביים לפונקציות של רשימות מקושרות ההבדל בין מערך לרשימה וריאציות של רשימות מקושרות תור ומחסנית

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