Download presentation
Presentation is loading. Please wait.
1
ADT של מבני נתונים ADT גנריים בחירת מבני נתונים שאלה לדוגמה
2
פתרון בעיה ישירות מבני נתונים מחסנית פתרון הבעיה בעזרת מחסנית מבוא לתכנות מערכות - 2341222
3
נרצה לקלוט 100 מספרים אי שליליים מהקלט ולהדפיס אותם בסדר הפוך בזמן הכנסת הקלט המשתמש יכול להתחרט ולבטל את הכנסת המספר האחרון – לצורך כך הוא צריך להכניס 1- פעולת הביטול דומה לפעולת “undo” בעורכי טקסטים המשתמש יכול לבצע “undo” כמה פעמים ולבטל כמה מספרים נפתור תחילה את הבעיה הזו ישירות מבוא לתכנות מערכות - 2341223
4
#include #define MAX_SIZE 100 #define UNDO_LAST_COMMAND -1 int main() { int input, size = 0, numbers[MAX_SIZE]; while (size < MAX_SIZE && scanf("%d", &input) == 1) { if (input != UNDO_LAST_COMMAND) { assert(size >= 0 && size < MAX_SIZE); numbers[size++] = input; continue; } if (size < 1) { printf("No numbers were entered until now\n"); continue; } size--; printf("undo\n"); } while (size > 0) { printf("%d\n", numbers[--size]); assert(size >= 0 && size < MAX_SIZE); } return 0; } מבוא לתכנות מערכות - 2341224
5
לא ניתן לעשות שימוש חוזר בקוד עבור בעיות דומות קל להכניס באגים – size-- או --size? – size++ או ++size? – size > 0 או size >= 0? – size < 1 או size < 0? הפתרון אינו מתעד את עצמו – מוסיפים רק לסוף המערך – מורידים מספרים רק מסוף המערך – ההדפסה מתבצעת רק בסדר הפוך עבור בעיה גדולה יותר, כבר לא ניתן לשמור על הקוד פשוט כמו במקרה זה מבוא לתכנות מערכות - 2341225
6
מבני נתונים מבני נתונים הם טיפוסי נתונים מיוחדים שמטרתם לשמור אוסף של משתנים ולאפשר עליהם פעולות מסוימות דוגמאות : – מערך – מערך - המנשק של מערך כולל קריאת איברים לפי אינדקס והשמה לאיברים לפי אינדקס – רשימה מקושרת – רשימה מקושרת - המנשק של רשימה מקושרת כולל קריאת איברים מהרשימה והכנסה / הוצאה של איברים מכל מקום ברשימה נוח לכתוב מבני נתונים נוספים כטיפוס נתונים ולהשתמש בהם עבור בעיות מתאימות מבוא לתכנות מערכות - 2341226
7
מחסנית מבנה הנתונים מחסנית מוגדר לפי המנשק הבא : push push - הוסף איבר למחסנית pop pop - הוצא את האיבר האחרון שהוכנס למחסנית ( מבלי להחזיר את ערכו ) top top - החזר את ערכו של האיבר האחרון שהוכנס למחסנית ( מבלי להוציאו ) מחסנית מאפשרת גישה רק לאיבר האחרון שהוכנס ורק אותו ניתן להוציא ברגע נתון (LIFO - Last In First Out) המחשת מחסנית : http://www.cosc.canterbury.ac.nz/people/mukundan/dsal/StackAppl.html מבוא לתכנות מערכות - 2341227
8
#ifndef _STACK_H #define _STACK_H /** ADT of Stack of integers */ typedef struct Stack_t* Stack; /** possible return values */ typedef enum { STACK_BAD_ARGUMENT, STACK_SUCCESS, STACK_EMPTY, STACK_FULL } StackResult; /** creates a Stack with maximal capacity of 'maxSize'. if fails, returns NULL */ Stack stackCreate(int maxSize); /** releases the memory allocated for the stack */ void stackDestroy(Stack stack); מבוא לתכנות מערכות - 2341228 לא לשכוח הגנה נגד include כפול איפה המבנה עצמו ? מדוע ? איפה המבנה עצמו ? מדוע ? ערכי שגיאות מוסכמים כדי לאפשר למשתמש להתמודד עם שגיאות
9
/** insert a number to the top of the stack. Error Codes: STACK_BAD_ARGUMENT if stack is NULL STACK_FULL if the stack is full. */ StackResult stackPush(Stack stack, int number); /** removes the element at the top of the stack. Error codes: STACK_BAD_ARGUMENT if stack is NULL STACK_EMPTY if the stack is empty */ StackResult stackPop(Stack stack) /** returns in 'number' the last element that was pushed. Error codes: STACK_BAD_ARGUMENT if stack or number are NULL STACK_EMPTY if the stack is empty */ StackResult stackTop(Stack stack, int* number) /** returns the number of elements in the stack. stack must not be NULL */ int stackSize(Stack stack) #endif מבוא לתכנות מערכות - 2341229
10
#include #include "stack.h" #define MAX_INPUT_SIZE 100 #define UNDO_LAST_COMMAND -1 int main() { Stack stack = stackCreate(MAX_INPUT_SIZE); if (stack == NULL) { fprintf(stderr, "failed to create stack\n"); return -1; } int input; while (stackSize(stack) < MAX_INPUT_SIZE && scanf("%d", &input) == 1) { if (input != UNDO_LAST_COMMAND) { StackResult result = stackPush(stack, input); assert(result == STACK_SUCCESS); continue; } StackResult result = stackPop(stack); if (result == STACK_EMPTY) { printf("No numbers were entered until now\n"); } else { assert(result == STACK_SUCCESS); printf("undo\n"); } } מבוא לתכנות מערכות - 23412210
11
while (stackSize(stack) > 0) { int number; StackResult result = stackTop(stack, &number); StackResult result2 = stackPop(stack); assert (result == STACK_SUCCESS && result2 == STACK_SUCCESS); printf("%d\n", number); } stackDestroy(stack); return 0; } מבוא לתכנות מערכות - 23412211
12
22 1717 33 נבחר לממש את המחסנית בעזרת מערך נשמור שלושה שדות במבנה – מערך בו יישמרו המספרים – גודל המחסינת המקסימלי – אינדקס המקום הפנוי הבא במערך זהו גם מספר האיברים במבנה איזו דרך נוספת קיימת למימוש מחסנית ? מבוא לתכנות מערכות - 23412212 nextIndex 55
13
#include #include "stack.h" /** The Stack is implemented as an array of integers. * With nextIndex as an index to the next available position and * the maximal size is stored in maxSize. */ struct Stack_t { int* array; int nextIndex; int maxSize; }; מבוא לתכנות מערכות - 23412213
14
Stack stackCreate(int maxSize) { if (maxSize <= 0) { return NULL; } Stack stack = malloc(sizeof(*stack)); if (stack == NULL) { return NULL; } stack->array = malloc(sizeof(int)*maxSize); if (stack->array == NULL) { free(stack); return NULL; } stack->nextIndex = 0; stack->maxSize = maxSize; return stack; } מבוא לתכנות מערכות - 23412214 שימו לב, בשלב זה כבר יש הקצאה שהצליחה
15
StackResult stackPush(Stack stack, int number) { if (stack == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex >= stack->maxSize) { return STACK_FULL; } assert(stack->nextIndex >= 0 && stack->nextIndex maxSize); stack->array[stack->nextIndex++] = number; return STACK_SUCCESS; } StackResult stackPop(Stack stack) { if (stack == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex < 1) { return STACK_EMPTY; } stack->nextIndex--; return STACK_SUCCESS; } מבוא לתכנות מערכות - 23412215
16
StackResult stackTop(Stack stack, int* number) { if (stack == NULL || number == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex < 1) { return STACK_EMPTY; } assert(stack->nextIndex > 0 && stack->nextIndex maxSize); *number = stack->array[stack->nextIndex - 1]; return STACK_SUCCESS; } int stackSize(Stack stack) { assert(stack); return stack->nextIndex; } void stackDestroy(Stack stack) { if (stack != NULL) { free(stack->array); free(stack); } } מבוא לתכנות מערכות - 23412216 כיצד ניתן לכתוב את פונקציה זו בצורה שונה כך שתחזיר ערכי שגיאה ? מה היתרונות והחסרונות של כל שיטה ? כיצד ניתן לכתוב את פונקציה זו בצורה שונה כך שתחזיר ערכי שגיאה ? מה היתרונות והחסרונות של כל שיטה ?
17
ניתן להגדיר מבני נתונים כ -ADT ע " י פתרון הבעיה עם מבנה המחסנית מתקבל פתרון עם סיכוי קטן יותר לבאגים הפתרון עם המחסנית מתעד את עצמו שימוש במבני נתונים מונע שכפול קוד שימוש במבני נתונים מבטיח את אופן העבודה עם הנתונים שימוש במבני הנתונים מקל על המתכנת בכתיבת קוד מבוא לתכנות מערכות - 23412217
18
מבני נתונים גנריים מחסנית גנרית שימוש במחסנית הגנרית מבוא לתכנות מערכות - 23412218
19
המחסנית שלנו מתאימה רק למספרים שלמים – בדרך כלל נשתמש בטיפוסים מורכבים יותר – נצטרך לשכפל את המחסנית לכל טיפוס נכתוב את המחסנית מחדש כמבנה נתונים גנרי המסוגל להחזיק עצמים מכל סוג – אלו תכונות של העצמים נצטרך כדי לשמור אותם במחסנית ? – כיצד ניתן לספק את תכונות אלו למחסנית מבלי לפגוע בגנריות ? מבוא לתכנות מערכות - 23412219
20
#ifndef _STACK_H #define _STACK_H /** generic ADT of Stack of integers */ typedef struct Stack_t* Stack; typedef void* Element; typedef Element (*CopyFunction)(Element); typedef void (*FreeFunction)(Element); /** possible return values */ typedef enum { STACK_BAD_ARGUMENT, STACK_SUCCESS, STACK_FAIL, STACK_EMPTY, STACK_FULL } StackResult; מבוא לתכנות מערכות - 23412220 typedef כדי להקל על המשתמש במבנה קוד שגיאה להתמודדות עם שגיאות בפונקציות הנשלחות ע " י המשתמש
21
/** creates a Stack with maximal capacity of 'maxSize'. if fails, returns NULL */ Stack stackCreate(int maxSize, CopyFunction copyFunction, FreeFunction freeFunction); /** releases the memory allocated for the stack */ void stackDestroy(Stack stack); /** inserts an element to the top of the stack. Error Codes: STACK_BAD_ARGUMENT if stack is NULL STACK_FULL if the stack is full and STACK_FAIL if the supplied copy function fails. */ StackResult stackPush(Stack stack, Element element); מבוא לתכנות מערכות - 23412221
22
/** removes the element at the top of the stack. Error codes: STACK_BAD_ARGUMENT if stack is NULL STACK_EMPTY if the stack is empty */ StackResult stackPop(Stack stack); /** returns in 'element' the last element that was pushed. Error codes: STACK_BAD_ARGUMENT if stack or number are NULL STACK_EMPTY if the stack is empty and STACK_FAIL if the supplied copy function fails */ StackResult stackTop(Stack stack, Element* element); /** returns the number of elements in the stack. stack must not be NULL */ int stackSize(Stack stack); #endif מבוא לתכנות מערכות - 23412222
23
#include #include "stack.h" #define MAX_INPUT_SIZE 10 #define UNDO_LAST_COMMAND -1 /* functions that will be used by the stack */ Element copyInt(Element element) { if (element == NULL) { return NULL; } int* newInt = malloc(sizeof(int)); if (newInt == NULL) { return NULL; } *newInt = *(int*)element; return newInt; } void freeInt(Element element) { free(element); } מבוא לתכנות מערכות - 23412223
24
int main() { Stack stack = stackCreate(MAX_INPUT_SIZE, copyInt, freeInt); if (stack == NULL) { fprintf(stderr, "failed to create stack\n"); return -1; } int input; while (stackSize(stack) < MAX_INPUT_SIZE && scanf("%d", &input) == 1) { if (input != UNDO_LAST_COMMAND) { StackResult result = stackPush(stack, &input); assert(result == STACK_SUCCESS); continue; } StackResult result = stackPop(stack); if (result == STACK_EMPTY) { printf("No numbers were entered until now\n"); } else { assert(result == STACK_SUCCESS); printf("undo\n"); } } מבוא לתכנות מערכות - 23412224
25
while (stackSize(stack) > 0) { int* number = NULL; StackResult result = stackTop(stack, (Element*)&number); StackResult result2 = stackPop(stack); assert(result == STACK_SUCCESS && result2 == STACK_SUCCESS); printf("%d\n", *number); freeInt(number); } stackDestroy(stack); return 0; } מבוא לתכנות מערכות - 23412225
26
מחרוזות נניח שהפעם אנחנו רוצים לקלוט מהקלט מחרוזות שמייצגות פקודות – גודל מחרוזת מקסימלי הוא 80 הפוך בסוף קליטת הפקודות התוכנית תדפיס את הפקודות בסדר הפוך “UNDO” אחת הפקודות יכולה להיות “UNDO” - היא מבטלת קליטת פקודה קודמת – פקודת UNDO אינה נקלטת ואינה מודפסת בסוף התוכנית מבוא לתכנות מערכות - 23412226
27
#include #include #include #include #include "stack.h" #define MAX_INPUT_SIZE 100 #define UNDO_COMMAND "UNDO" #define MAX_COMMAND_SIZE 80 /* functions that will be used by the stack */ Element copyString(Element element) { if (element == NULL) { return NULL; } char* newString = malloc (strlen(element) + 1); if (newString == NULL) { return NULL; } return strcpy(newString, element); } void freeString(Element element) { free (element); } מבוא לתכנות מערכות - 23412227
28
int main() { Stack stack = stackCreate(MAX_INPUT_SIZE, copyString, freeString); if (stack == NULL) { fprintf(stderr, "failed to create stack\n"); return -1; } char input[MAX_COMMAND_SIZE] = ""; while (stackSize(stack) < MAX_INPUT_SIZE && scanf("%s", input) == 1) { if (strcmp(input,UNDO_COMMAND) != 0) { StackResult result = stackPush(stack, input); assert(result == STACK_SUCCESS); continue; } StackResult result = stackPop(stack); if (result == STACK_EMPTY) { printf("No numbers were entered until now\n"); } else { assert(result == STACK_SUCCESS); printf("undo\n"); } } מבוא לתכנות מערכות - 23412228
29
while (stackSize(stack) > 0) { char* command = NULL; StackResult result = stackTop(stack, (Element*)&command); StackResult result2 = stackPop(stack); assert(result == STACK_SUCCESS && result2 == STACK_SUCCESS); printf("%s\n", command); freeString(command); } stackDestroy(stack); return 0; } מבוא לתכנות מערכות - 23412229
30
#include #include "stack.h" /** The Stack is implemented as an array of Elements. * With nextIndex as an index to the next available position and * maximal size stored in maxsize. */ struct Stack_t { Element* array; int nextIndex; int maxSize; CopyFunction copyElement; FreeFunction freeElement; }; מבוא לתכנות מערכות - 23412230
31
Stack stackCreate(int maxSize, CopyFunction copyFunction, FreeFunction freeFunction) { if (maxSize <= 0 || !copyFunction || !freeFunction) { return NULL; } Stack stack = malloc(sizeof(*stack)); if (stack == NULL) { return NULL; } stack->array = malloc(sizeof(Element) * maxSize); if (stack->array == NULL) { free(stack); return NULL; } stack->nextIndex = 0; stack->maxSize = maxSize; stack->copyElement = copyFunction; stack->freeElement = freeFunction; return stack; } מבוא לתכנות מערכות - 23412231
32
StackResult stackPush(Stack stack, Element element) { if (stack == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex >= stack->maxSize) { return STACK_FULL; } Element newElement = stack->copyElement(element); if (newElement == NULL) { return STACK_FAIL; } assert(stack->nextIndex >= 0 && stack->nextIndex maxSize); stack->array[stack->nextIndex++] = newElement; return STACK_SUCCESS; } מבוא לתכנות מערכות - 23412232
33
StackResult stackPop(Stack stack) { if (stack == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex < 1) { return STACK_EMPTY; } assert(stack->nextIndex > 0 && stack->nextIndex maxSize); stack->freeElement(stack->array[stack->nextIndex - 1]); stack->nextIndex--; return STACK_SUCCESS; } מבוא לתכנות מערכות - 23412233
34
StackResult stackTop(Stack stack, Element* element) { if (stack == NULL || element == NULL) { return STACK_BAD_ARGUMENT; } if (stack->nextIndex < 1) { return STACK_EMPTY; } assert(stack->nextIndex > 0 && stack->nextIndex maxSize); Element newElement = stack->copyElement(stack->array[stack->nextIndex - 1]); if (newElement == NULL) { return STACK_FAIL; } *element = newElement; return STACK_SUCCESS; } מבוא לתכנות מערכות - 23412234 למה יוצרים העתק של העצם המוחזר ?
35
int stackSize(Stack stack) { assert(stack); return stack->nextIndex; } void stackDestroy(Stack stack) { if (stack == NULL) { return; } while (stackSize(stack) > 0) { StackResult result = stackPop(stack); assert(result == STACK_SUCCESS); } free(stack->array); free(stack); } מבוא לתכנות מערכות - 23412235
36
ניתן ליצור מבני נתונים גנריים המסוגלים לשמור כל סוג של עצמים כדי לאפשר למבני נתונים גנריים לעבור עם סוג מסוים של עצמים יש לספק להם מצביעים לפונקציות לביצוע הפעולות הבסיסיות שימוש במבני נתונים גנריים מאפשר שימוש חוזר במבנה עבור טיפוסים שונים ומונע שכפול קוד מבוא לתכנות מערכות - 23412236
37
בחירת מבני נתונים מבני הנתונים הנלמדים בקורס מבוא לתכנות מערכות - 23412237
38
מבני הנתונים הבסיסיים בקורס זה אנו לומדים את מבני הנתונים הבסיסיים הבאים ( כולם נלמדים כ -ADT): 1.List - רשימה 1.List - רשימה : שומרת אוסף איברים עם סדר ביניהם ומאפשרת הכנסת אותו איבר מספר פעמים 2.Set - קבוצה 2.Set - קבוצה : מאפשרת הכנסת איבר פעם אחת בלבד ואינה שומרת סדר בין איברי הקבוצה 3.Stack - מחסנית 3.Stack - מחסנית : מאפשרת הכנסה, גישה והוצאה רק מסופה. שומרת על סדר ומאפשרת כפילויות 4.Graph - גרף 4.Graph - גרף : שומר קבוצת צמתים וקבוצת קשתות המחברות ביניהם – מתאים לבעיות הדורשות אבסטרקציה של רשתות כגון רשת כבישים, רשת מחשבים וכו '. מבוא לתכנות מערכות - 23412238
39
מבנה הנתונים המתאים ביותר לכל בעיה חשוב להתאים את מבנה הנתונים המתאים ביותר התאמת מבנה הנתונים נעשית לפי שני שיקולים עיקריים : קוד קצר יותרפשוט יותרמונעת שכפול קוד מקשה על הכנסת באגים – איכות הקוד - בחירה טובה יוצרת קוד קצר יותר, פשוט יותר, מונעת שכפול קוד ומקשה על הכנסת באגים למשל בחירת set במקום list מונעת הכנסת איבר פעמיים, חוסכת התעסקות בסדר הרשימה ובדיקות לפני הכנסת איבר בשנית שיקול זה לא יעניין אותנו בקורס זה ויילמד לעומק בקורס מבני נתונים – סיבוכיות - בחירת מבנה כך שהפעולות הקריטיות מהירות. שיקול זה לא יעניין אותנו בקורס זה ויילמד לעומק בקורס מבני נתונים בבחירת המבנה כדאי להתחשב בדברים הבאים : – האם יש כפילויות ? – האם צריך לשמור סדר שונה בכל פעם ? – האם ניתן לקחת מבנה ספציפי יותר כך שייחסכו בדיקות מיותרות ? מבוא לתכנות מערכות - 23412239
40
מבני הנתונים הנלמדים בקורס הם List, Set, Stack ו -Graph יש לבחור מבנה נתונים מתאים לבעיה כדי להקל על העבודה מבוא לתכנות מערכות - 23412240
41
מבוא לתכנות מערכות - 23412241
42
מבנה הנתונים ערמה מאפשר הכנסת איברים והוצאה של האיבר " המקסימלי " לפי סדר שהוגדר. כלומר הפעולות הנדרשות מערמה הן : 1. יצירת ערמה חדשה. 2. שחרור ערמה קיימת. 3. הכנסת איבר לערמה, ניתן להכניס מספר עותקים של אותו איבר. 4. הוצאת האיבר המקסימלי מהערמה. במקרה והערמה ריקה תוחזר שגיאה. א. כתבו את קובץ המנשק עבור ADT של ערמה ב. באילו מה -ADT שנלמדו בקורס כדאי להשתמש למימוש בערמה ? מדוע ? ג. כתבו את הקוד הדרוש למימוש ה -struct עבור הערמה ד. ממשו את הפונקציה עבור יצירת ערמה חדשה מבוא לתכנות מערכות - 23412242
43
#ifndef _HEAP_H #define _HEAP_H #include typedef struct heap_t* Heap; typedef enum { HEAP_SUCCESS, HEAP_NULL_ARGUMENT, HEAP_OUT_OF_MEMORY, HEAP_EMPTY } HeapResult; Heap heapCreate(void* (*copy)(void*), void(*release)(void*), bool (*compare)(void*,void*)); HeapResult heapPush(Heap heap, void* element); HeapResult heapPop(Heap heap, void** element); void heapDestroy(Heap heap); #endif מבוא לתכנות מערכות - 23412243 ניתן להגדיר את המצביעים ישירות או להוסיף typedef מתאימים
44
נבחר להשתמש ב -List עבור מימוש הערמה : – ייתכנו העתקים של אותו איבר בערמה – יהיה לנו נוח יותר להוציא את האיבר ששמור בראש הרשימה מימוש המבנה בקובץ ה -C: struct heap_t { List items; bool (*compare)(void*,void*); }; מבוא לתכנות מערכות - 23412244 איפה יישמרו המצביעים לשאר לפונקציות ?
45
Heap heapCreate(void* (*copy)(void*), void (*release)(void*), bool (*compare)(void*,void*)) { if (!copy || !release || !compare) { return NULL; } Heap heap = malloc(sizeof(*heap)); if (!heap) { return NULL; } heap->items = listCreate(copy, release); if (!heap->items) { heapDestroy(heap); return NULL; } heap->compare = compare; return heap; } מבוא לתכנות מערכות - 23412245
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.