adt של מבני נתונים adt גנריים בחירת מבני נתונים שאלה...

Post on 19-Dec-2015

241 Views

Category:

Documents

1 Downloads

Preview:

Click to see full reader

TRANSCRIPT

5תרגול מס' ADTשל מבני נתונים ADTגנריים בחירת מבני נתוניםשאלה לדוגמה

2מבוא לתכנות מערכות - 234122

ADTשל מבני נתונים

פתרון בעיה ישירותמבני נתוניםמחסניתפתרון הבעיה בעזרת מחסנית

3מבוא לתכנות מערכות - 234122

בעיה לדוגמה

מספרים אי שליליים מהקלט ולהדפיס אותם בסדר 100נרצה לקלוט הפוך

בזמן הכנסת הקלט המשתמש יכול להתחרט ולבטל את הכנסת המספרהאחרון

-1לצורך כך הוא צריך להכניס –

פעולת הביטול דומה לפעולת“undoבעורכי טקסטים ”

“ המשתמש יכול לבצעundoכמה פעמים ולבטל כמה מספרים ”

נפתור תחילה את הבעיה הזו ישירות

4מבוא לתכנות מערכות - 234122

פתרון ישיר#include <stdio.h>#include <assert.h>#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;

}

5מבוא לתכנות מערכות - 234122

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

קל להכניס באגים–size-- או --size?

–size++ או ++size?

– size > 0 אוsize >= 0?

–size < 1 או size < 0?

הפתרון אינו מתעד את עצמומוסיפים רק לסוף המערך–

מורידים מספרים רק מסוף המערך–

ההדפסה מתבצעת רק בסדר הפוך–

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

6מבוא לתכנות מערכות - 234122

מבני נתונים

הם טיפוסי נתונים מיוחדים שמטרתם לשמור אוסף של מבני נתוניםמשתנים ולאפשר עליהם פעולות מסוימות

:דוגמאות- המנשק של מערך כולל קריאת איברים לפי אינדקס והשמה מערך–

לאיברים לפי אינדקס

- המנשק של רשימה מקושרת כולל קריאת איברים רשימה מקושרת –מהרשימה והכנסה/הוצאה של איברים מכל מקום ברשימה

נוח לכתוב מבני נתונים נוספים כטיפוס נתונים ולהשתמש בהם עבורבעיות מתאימות

7מבוא לתכנות מערכות - 234122

Stackמחסנית - מוגדר לפי המנשק הבא:מחסניתמבנה הנתונים push הוסף איבר למחסנית -pop הוצא את האיבר האחרון שהוכנס למחסנית )מבלי -

להחזיר את ערכו(top החזר את ערכו של האיבר האחרון שהוכנס -

למחסנית )מבלי להוציאו(

מחסנית מאפשרת גישה רק לאיבר האחרון שהוכנס ורק(LIFO - Last In First Outאותו ניתן להוציא ברגע נתון )

:המחשת מחסניתhttp://

www.cosc.canterbury.ac.nz/people/mukundan/dsal/StackAppl.html

8מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.h#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);

לא לשכוח הגנה איפה המבנה כפולincludeנגד

עצמו?מדוע?

ערכי שגיאות מוסכמים כדי לאפשר למשתמש להתמודד עם שגיאות

9מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.h/** 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

10מבוא לתכנות מערכות - 234122

פתרון הבעיה בעזרת מחסנית#include <stdio.h>#include <assert.h>#include <stdlib.h>#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");

}}

11מבוא לתכנות מערכות - 234122

פתרון הבעיה בעזרת מחסנית 

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;

}

12מבוא לתכנות מערכות - 234122

2

17

3

מימוש המחסנית

נבחר לממש את המחסנית בעזרת מערך

נשמור שלושה שדות במבנהמערך בו יישמרו המספרים–

גודל המחסינת המקסימלי–

אינדקס המקום הפנוי הבא במערך–

זהו גם מספר האיברים במבנה•

?איזו דרך נוספת קיימת למימוש מחסנית

nextIndex

5

13מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.c#include <stdlib.h>#include <assert.h>#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;

}; 

14מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.cStack 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;

שימו לב, בשלב זה כבר יש הקצאה

שהצליחה

15מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.cStackResult 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 < stack->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;

}

16מבוא לתכנות מערכות - 234122

ADT - מחסנית stack.cStackResult 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 <= stack->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);

}}

כיצד ניתן לכתוב את פונקציה זו בצורה שונה כך שתחזיר ערכי שגיאה?

מה היתרונות והחסרונות של כל שיטה?

17מבוא לתכנות מערכות - 234122

ADTשל מבני נתונים - סיכום

-ניתן להגדיר מבני נתונים כADT ע"י פתרון הבעיה עם מבנה המחסנית מתקבל פתרון עם סיכוי קטן יותר

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

18מבוא לתכנות מערכות - 234122

ADTגנריים

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

19מבוא לתכנות מערכות - 234122

מבני נתונים גנריים

המחסנית שלנו מתאימה רק למספרים שלמיםבדרך כלל נשתמש בטיפוסים מורכבים יותר–

נצטרך לשכפל את המחסנית לכל טיפוס–

נכתוב את המחסנית מחדש כמבנה נתונים גנרי המסוגל להחזיק עצמיםמכל סוג

אלו תכונות של העצמים נצטרך כדי לשמור אותם במחסנית?–

כיצד ניתן לספק את תכונות אלו למחסנית מבלי לפגוע בגנריות?–

20מבוא לתכנות מערכות - 234122

stack.hמחסנית גנרית - #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;

typedef כדי להקל על המשתמש

במבנה

קוד שגיאה להתמודדות עם שגיאות בפונקציות הנשלחות ע"י

המשתמש

21מבוא לתכנות מערכות - 234122

stack.hמחסנית גנרית - /** 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);

22מבוא לתכנות מערכות - 234122

stack.hמחסנית גנרית - /** 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 

23מבוא לתכנות מערכות - 234122

פתרון הבעיה בעזרת מחסנית גנרית#include <stdio.h>#include <assert.h>#include <stdlib.h>#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);

}

24מבוא לתכנות מערכות - 234122

פתרון הבעיה בעזרת מחסנית גנרית

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");

}}

25מבוא לתכנות מערכות - 234122

פתרון הבעיה בעזרת מחסנית גנרית

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;

}

26מבוא לתכנות מערכות - 234122

שימוש בגנריות

שמייצגות פקודותמחרוזותנניח שהפעם אנחנו רוצים לקלוט מהקלט 80גודל מחרוזת מקסימלי הוא –

הפוךבסוף קליטת הפקודות התוכנית תדפיס את הפקודות בסדר

אחת הפקודות יכולה להיות“UNDO” היא מבטלת קליטת פקודה - קודמת

אינה נקלטת ואינה מודפסת בסוף התוכניתUNDOפקודת –

27מבוא לתכנות מערכות - 234122

שימוש במחסנית גנרית#include <stdio.h>#include <assert.h>#include <stdlib.h>#include <string.h>#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);}

28מבוא לתכנות מערכות - 234122

שימוש במחסנית גנרית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");}}

29מבוא לתכנות מערכות - 234122

שימוש במחסנית גנרית

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;

}

30מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - #include <stdlib.h>#include <assert.h>#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;

}; 

31מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - 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;

}

32מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - 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 < stack-

>maxSize);stack->array[stack->nextIndex++] = newElement;return STACK_SUCCESS;

33מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - StackResult stackPop(Stack stack) {

if (stack == NULL) {return STACK_BAD_ARGUMENT;

}if (stack->nextIndex < 1) {

return STACK_EMPTY;}

 assert(stack->nextIndex > 0 && stack->nextIndex <= stack-

>maxSize);stack->freeElement(stack->array[stack->nextIndex - 1]);stack->nextIndex--;return STACK_SUCCESS;

}

34מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - 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 <= stack-

>maxSize);Element newElement = stack->copyElement(stack->array[stack-

>nextIndex - 1]);if (newElement == NULL) {

return STACK_FAIL;}*element = newElement;return STACK_SUCCESS;

}

למה יוצרים העתק של העצם המוחזר?

35מבוא לתכנות מערכות - 234122

stack.cמימוש המחסנית גנרית - 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);

}

36מבוא לתכנות מערכות - 234122

ADTגנריים - סיכום

ניתן ליצור מבני נתונים גנריים המסוגלים לשמור כל סוג של עצמים כדי לאפשר למבני נתונים גנריים לעבור עם סוג מסוים של עצמים יש

לספק להם מצביעים לפונקציות לביצוע הפעולות הבסיסיות שימוש במבני נתונים גנריים מאפשר שימוש חוזר במבנה עבור טיפוסים

שונים ומונע שכפול קוד

37מבוא לתכנות מערכות - 234122

בחירת מבני נתונים

בחירת מבני נתוניםמבני הנתונים הנלמדים בקורס

38מבוא לתכנות מערכות - 234122

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

הבאים )כולם מבני הנתונים הבסיסיים בקורס זה אנו לומדים את(:ADTנלמדים כ-

.1Listשומרת אוסף איברים עם סדר ביניהם ומאפשרת הכנסת - רשימה :אותו איבר מספר פעמים

.2Setמאפשרת הכנסת איבר פעם אחת בלבד ואינה שומרת - קבוצה :סדר בין איברי הקבוצה

.3Stackמאפשרת הכנסה, גישה והוצאה רק מסופה. שומרת - מחסנית :על סדר ומאפשרת כפילויות

.4Graphשומר קבוצת צמתים וקבוצת קשתות המחברות ביניהם - גרף :מתאים לבעיות הדורשות אבסטרקציה של רשתות כגון רשת כבישים, רשת –

מחשבים וכו'.

39מבוא לתכנות מערכות - 234122

התאמת מבנה נתונים לבעיה מבנה הנתונים המתאים ביותרלכל בעיה חשוב להתאים את

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

מקשה על הכנסת באגיםו

מונעת הכנסת איבר פעמיים, חוסכת התעסקות list במקום setלמשל בחירת •בסדר הרשימה ובדיקות לפני הכנסת איבר בשנית

שיקול זה לא יעניין סיבוכיות - בחירת מבנה כך שהפעולות הקריטיות מהירות. –אותנו בקורס זה ויילמד לעומק בקורס מבני נתונים

:בבחירת המבנה כדאי להתחשב בדברים הבאיםהאם יש כפילויות?–

האם צריך לשמור סדר שונה בכל פעם?–

האם ניתן לקחת מבנה ספציפי יותר כך שייחסכו בדיקות מיותרות?–

40מבוא לתכנות מערכות - 234122

בחירת מבני נתונים - סיכום

מבני הנתונים הנלמדים בקורס הםList, Set, Stack-ו Graphיש לבחור מבנה נתונים מתאים לבעיה כדי להקל על העבודה

41מבוא לתכנות מערכות - 234122

ADTשאלה לדוגמה -

42מבוא לתכנות מערכות - 234122

שאלה לדוגמה

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

יצירת ערמה חדשה.1.

שחרור ערמה קיימת.2.

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

הוצאת האיבר המקסימלי מהערמה. במקרה והערמה ריקה תוחזר שגיאה.4.

של ערמהADTא. כתבו את קובץ המנשק עבור

שנלמדו בקורס כדאי להשתמש למימוש בערמה? מדוע?ADTב. באילו מה-

עבור הערמהstructג. כתבו את הקוד הדרוש למימוש ה-

ד. ממשו את הפונקציה עבור יצירת ערמה חדשה

43מבוא לתכנות מערכות - 234122

סעיף א'#ifndef _HEAP_H

#define _HEAP_H

#include <stdbool.h>

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

ניתן להגדיר את המצביעים typedefישירות או להוסיף מתאימים

44מבוא לתכנות מערכות - 234122

סעיפים ב' ו-ג'

-נבחר להשתמש בList:עבור מימוש הערמה ייתכנו העתקים של אותו איבר בערמה–

יהיה לנו נוח יותר להוציא את האיבר ששמור בראש הרשימה–

-מימוש המבנה בקובץ הC:

struct heap_t {

List items;

bool (*compare)(void*,void*);

};איפה יישמרו המצביעים

לשאר לפונקציות?

45מבוא לתכנות מערכות - 234122

סעיף ד'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;

}

top related