450 likes | 762 Views
תרגול מס' 5. ADT של מבני נתונים ADT גנריים בחירת מבני נתונים שאלה לדוגמה. ADT של מבני נתונים. פתרון בעיה ישירות מבני נתונים מחסנית פתרון הבעיה בעזרת מחסנית. בעיה לדוגמה. נרצה לקלוט 100 מספרים אי שליליים מהקלט ולהדפיס אותם בסדר הפוך
E N D
תרגול מס' 5 ADT של מבני נתונים ADT גנריים בחירת מבני נתונים שאלה לדוגמה
ADT של מבני נתונים פתרון בעיה ישירות מבני נתונים מחסנית פתרון הבעיה בעזרת מחסנית מבוא לתכנות מערכות - 234122
בעיה לדוגמה • נרצה לקלוט 100 מספרים אי שליליים מהקלט ולהדפיס אותם בסדר הפוך • בזמן הכנסת הקלט המשתמש יכול להתחרט ולבטל את הכנסת המספר האחרון • לצורך כך הוא צריך להכניס 1- • פעולת הביטול דומה לפעולת “undo” בעורכי טקסטים • המשתמש יכול לבצע “undo” כמה פעמים ולבטל כמה מספרים • נפתור תחילה את הבעיה הזו ישירות מבוא לתכנות מערכות - 234122
פתרון ישיר • #include<stdio.h> • #include<assert.h> • #define MAX_SIZE 100 • #defineUNDO_LAST_COMMAND -1 • intmain() { • 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; • } מבוא לתכנות מערכות - 234122
חסרונות הפתרון הישיר • לא ניתן לעשות שימוש חוזר בקוד עבור בעיות דומות • קל להכניס באגים • size-- או --size? • size++או ++size? • size > 0או size >= 0? • size < 1 או size < 0? • הפתרון אינו מתעד את עצמו • מוסיפים רק לסוף המערך • מורידים מספרים רק מסוף המערך • ההדפסה מתבצעת רק בסדר הפוך • עבור בעיה גדולה יותר, כבר לא ניתן לשמור על הקוד פשוט כמו במקרה זה מבוא לתכנות מערכות - 234122
מבני נתונים • מבני נתונים הם טיפוסי נתונים מיוחדים שמטרתם לשמור אוסף של משתנים ולאפשר עליהם פעולות מסוימות • דוגמאות: • מערך- המנשק של מערך כולל קריאת איברים לפי אינדקס והשמה לאיברים לפי אינדקס • רשימה מקושרת - המנשק של רשימה מקושרת כולל קריאת איברים מהרשימה והכנסה/הוצאה של איברים מכל מקום ברשימה • נוח לכתוב מבני נתונים נוספים כטיפוס נתונים ולהשתמש בהם עבור בעיות מתאימות מבוא לתכנות מערכות - 234122
מחסנית - Stack • מבנה הנתונים מחסניתמוגדר לפי המנשק הבא: • push- הוסף איבר למחסנית • pop- הוצא את האיבר האחרון שהוכנס למחסנית (מבלי להחזיר את ערכו) • top- החזר את ערכו של האיבר האחרון שהוכנס למחסנית (מבלי להוציאו) • מחסנית מאפשרת גישה רק לאיבר האחרון שהוכנס ורק אותו ניתן להוציא ברגע נתון (LIFO - Last In First Out) • המחשת מחסנית: http://www.cosc.canterbury.ac.nz/people/mukundan/dsal/StackAppl.html מבוא לתכנות מערכות - 234122
ADT מחסנית - stack.h לא לשכוח הגנה נגד include כפול • #ifndef _STACK_H • #define _STACK_H • /** ADT of Stack of integers */ • typedefstructStack_t* Stack; • /** possible return values */ • typedefenum { • STACK_BAD_ARGUMENT, STACK_SUCCESS, STACK_EMPTY, STACK_FULL • } StackResult; • /** creates a Stack with maximal capacity of 'maxSize'. • if fails, returns NULL */ • StackstackCreate(intmaxSize); • /** releases the memory allocated for the stack */ • voidstackDestroy(Stack stack); איפה המבנה עצמו? מדוע? ערכי שגיאות מוסכמים כדי לאפשר למשתמש להתמודד עם שגיאות מבוא לתכנות מערכות - 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. */ • StackResultstackPush(Stackstack, 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 */ • StackResultstackPop(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 */ • StackResultstackTop(Stackstack, int* number) • /** returns the number of elements in the stack. • stack must not be NULL */ • intstackSize(Stack stack) • #endif מבוא לתכנות מערכות - 234122
פתרון הבעיה בעזרת מחסנית • #include<stdio.h> • #include<assert.h> • #include<stdlib.h> • #include"stack.h" • #define MAX_INPUT_SIZE 100 • #defineUNDO_LAST_COMMAND -1 • intmain() { • Stackstack = 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"); • } • } מבוא לתכנות מערכות - 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; • } מבוא לתכנות מערכות - 234122
מימוש המחסנית • נבחר לממש את המחסנית בעזרת מערך • נשמור שלושה שדות במבנה • מערך בו יישמרו המספרים • גודל המחסינת המקסימלי • אינדקס המקום הפנוי הבא במערך • זהו גם מספר האיברים במבנה • איזו דרך נוספת קיימת למימוש מחסנית? nextIndex 5 2 17 3 מבוא לתכנות מערכות - 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. • */ • structStack_t { • int* array; • intnextIndex; • intmaxSize; • }; מבוא לתכנות מערכות - 234122
ADT מחסנית - stack.c • StackstackCreate(intmaxSize) { • if (maxSize <= 0) { • return NULL; • } • Stackstack = 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; • } שימו לב, בשלב זה כבר יש הקצאה שהצליחה מבוא לתכנות מערכות - 234122
ADT מחסנית - stack.c • StackResultstackPush(Stackstack, int number) { • if (stack == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex >= stack->maxSize) { • returnSTACK_FULL; • } • assert(stack->nextIndex >= 0 && stack->nextIndex < stack->maxSize); • stack->array[stack->nextIndex++] = number; • returnSTACK_SUCCESS; • } • StackResultstackPop(Stack stack) { • if (stack == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex < 1) { • returnSTACK_EMPTY; • } • stack->nextIndex--; • returnSTACK_SUCCESS; • } מבוא לתכנות מערכות - 234122
ADT מחסנית - stack.c • StackResultstackTop(Stackstack, int* number) { • if(stack == NULL || number == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex < 1) { • returnSTACK_EMPTY; • } • assert(stack->nextIndex > 0 && stack->nextIndex <= stack->maxSize); • *number = stack->array[stack->nextIndex - 1]; • returnSTACK_SUCCESS; • } • intstackSize(Stack stack) { • assert(stack); • return stack->nextIndex; • } • voidstackDestroy(Stack stack) { • if (stack != NULL) { • free(stack->array); • free(stack); • } • } כיצד ניתן לכתוב את פונקציה זו בצורה שונה כך שתחזיר ערכי שגיאה? מה היתרונות והחסרונות של כל שיטה? מבוא לתכנות מערכות - 234122
ADT של מבני נתונים - סיכום • ניתן להגדיר מבני נתונים כ-ADT • ע"י פתרון הבעיה עם מבנה המחסנית מתקבל פתרון עם סיכוי קטן יותר לבאגים • הפתרון עם המחסנית מתעד את עצמו • שימוש במבני נתונים מונע שכפול קוד • שימוש במבני נתונים מבטיח את אופן העבודה עם הנתונים • שימוש במבני הנתונים מקל על המתכנת בכתיבת קוד מבוא לתכנות מערכות - 234122
ADT גנריים מבני נתונים גנריים מחסנית גנרית שימוש במחסנית הגנרית מבוא לתכנות מערכות - 234122
מבני נתונים גנריים • המחסנית שלנו מתאימה רק למספרים שלמים • בדרך כלל נשתמש בטיפוסים מורכבים יותר • נצטרך לשכפל את המחסנית לכל טיפוס • נכתוב את המחסנית מחדש כמבנה נתונים גנרי המסוגל להחזיק עצמים מכל סוג • אלו תכונות של העצמים נצטרך כדי לשמור אותם במחסנית? • כיצד ניתן לספק את תכונות אלו למחסנית מבלי לפגוע בגנריות? מבוא לתכנות מערכות - 234122
מחסנית גנרית - stack.h • #ifndef _STACK_H • #define _STACK_H • /** generic ADT of Stack of integers */ • typedefstructStack_t* Stack; • typedefvoid* Element; • typedefElement (*CopyFunction)(Element); • typedefvoid (*FreeFunction)(Element); • /** possible return values */ • typedefenum { • STACK_BAD_ARGUMENT, STACK_SUCCESS,STACK_FAIL, STACK_EMPTY, STACK_FULL • } StackResult; typedef כדי להקל על המשתמש במבנה קוד שגיאה להתמודדות עם שגיאות בפונקציות הנשלחות ע"י המשתמש מבוא לתכנות מערכות - 234122
מחסנית גנרית - stack.h • /** creates a Stack with maximal capacity of 'maxSize'. • if fails, returns NULL */ • StackstackCreate(intmaxSize, CopyFunctioncopyFunction, • FreeFunctionfreeFunction); • /** releases the memory allocated for the stack */ • voidstackDestroy(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. */ • StackResultstackPush(Stackstack, Element element); מבוא לתכנות מערכות - 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 */ • StackResultstackPop(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 */ • StackResultstackTop(Stackstack, Element* element); • /** returns the number of elements in the stack. • stack must not be NULL */ • intstackSize(Stack stack); • #endif מבוא לתכנות מערכות - 234122
פתרון הבעיה בעזרת מחסנית גנרית • #include<stdio.h> • #include<assert.h> • #include<stdlib.h> • #include"stack.h" • #define MAX_INPUT_SIZE 10 • #defineUNDO_LAST_COMMAND -1 • /* functions that will be used by the stack */ • ElementcopyInt(Element element) { • if (element == NULL) { • return NULL; • } • int* newInt = malloc(sizeof(int)); • if (newInt == NULL) { • return NULL; • } • *newInt = *(int*)element; • returnnewInt; • } • voidfreeInt(Element element) { • free(element); • } מבוא לתכנות מערכות - 234122
פתרון הבעיה בעזרת מחסנית גנרית • intmain() { • Stackstack = 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"); • } • } מבוא לתכנות מערכות - 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; • } מבוא לתכנות מערכות - 234122
שימוש בגנריות • נניח שהפעם אנחנו רוצים לקלוט מהקלט מחרוזותשמייצגות פקודות • גודל מחרוזת מקסימלי הוא 80 • בסוף קליטת הפקודות התוכנית תדפיס את הפקודות בסדר הפוך • אחת הפקודות יכולה להיות “UNDO” - היא מבטלת קליטת פקודה קודמת • פקודת UNDO אינה נקלטת ואינה מודפסת בסוף התוכנית מבוא לתכנות מערכות - 234122
שימוש במחסנית גנרית • #include<stdio.h>#include<assert.h>#include<stdlib.h>#include<string.h>#include"stack.h"#define MAX_INPUT_SIZE 100#defineUNDO_COMMAND "UNDO"#define MAX_COMMAND_SIZE 80 • /* functions that will be used by the stack */ • ElementcopyString(Element element) { • if (element == NULL) { • return NULL;}char* newString = malloc (strlen(element) + 1); • if (newString == NULL) { • return NULL;}returnstrcpy(newString, element);}voidfreeString(Element element) { • free (element);} מבוא לתכנות מערכות - 234122
שימוש במחסנית גנרית • intmain() { • Stackstack = 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");}} מבוא לתכנות מערכות - 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; • } מבוא לתכנות מערכות - 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. • */ • structStack_t { • Element* array; • intnextIndex; • intmaxSize; • CopyFunctioncopyElement; • FreeFunctionfreeElement; • }; מבוא לתכנות מערכות - 234122
מימוש המחסנית גנרית - stack.c • StackstackCreate(intmaxSize, CopyFunctioncopyFunction, FreeFunctionfreeFunction) { • if (maxSize <= 0 || !copyFunction || !freeFunction) { • return NULL;}Stackstack = 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; • } מבוא לתכנות מערכות - 234122
מימוש המחסנית גנרית - stack.c • StackResultstackPush(Stackstack, Element element) { • if (stack == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex >= stack->maxSize) { • returnSTACK_FULL; • } • ElementnewElement = stack->copyElement(element); • if (newElement == NULL) { • returnSTACK_FAIL; • } • assert(stack->nextIndex >= 0 && stack->nextIndex < stack->maxSize); • stack->array[stack->nextIndex++] = newElement; • returnSTACK_SUCCESS; • } מבוא לתכנות מערכות - 234122
מימוש המחסנית גנרית - stack.c • StackResultstackPop(Stack stack) { • if (stack == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex < 1) { • returnSTACK_EMPTY; • } • assert(stack->nextIndex > 0 && stack->nextIndex <= stack->maxSize); • stack->freeElement(stack->array[stack->nextIndex - 1]); • stack->nextIndex--; • returnSTACK_SUCCESS; • } מבוא לתכנות מערכות - 234122
מימוש המחסנית גנרית - stack.c • StackResultstackTop(Stackstack, Element* element) { • if(stack == NULL || element == NULL) { • returnSTACK_BAD_ARGUMENT; • } • if (stack->nextIndex < 1) { • returnSTACK_EMPTY; • } • assert(stack->nextIndex > 0 && stack->nextIndex <= stack->maxSize); • ElementnewElement = stack->copyElement(stack->array[stack->nextIndex - 1]); • if (newElement == NULL) { • returnSTACK_FAIL; • } • *element = newElement; • returnSTACK_SUCCESS; • } למה יוצרים העתק של העצם המוחזר? מבוא לתכנות מערכות - 234122
מימוש המחסנית גנרית - stack.c • intstackSize(Stack stack) { • assert(stack); • return stack->nextIndex; • } • voidstackDestroy(Stack stack) { • if (stack == NULL) { • return; • } • while (stackSize(stack) > 0) { • StackResult result = stackPop(stack); • assert(result == STACK_SUCCESS); • } • free(stack->array); • free(stack); • } מבוא לתכנות מערכות - 234122
ADTגנריים - סיכום • ניתן ליצור מבני נתונים גנריים המסוגלים לשמור כל סוג של עצמים • כדי לאפשר למבני נתונים גנריים לעבור עם סוג מסוים של עצמים יש לספק להם מצביעים לפונקציות לביצוע הפעולות הבסיסיות • שימוש במבני נתונים גנריים מאפשר שימוש חוזר במבנה עבור טיפוסים שונים ומונע שכפול קוד מבוא לתכנות מערכות - 234122
בחירת מבני נתונים בחירת מבני נתונים מבני הנתונים הנלמדים בקורס מבוא לתכנות מערכות - 234122
מבני הנתונים הנלמדים בקורס • בקורס זה אנו לומדים את מבני הנתונים הבסיסיים הבאים (כולם נלמדים כ-ADT): • List - רשימה: שומרת אוסף איברים עם סדר ביניהם ומאפשרת הכנסת אותו איבר מספר פעמים • Set - קבוצה: מאפשרת הכנסת איבר פעם אחת בלבד ואינה שומרת סדר בין איברי הקבוצה • Stack - מחסנית: מאפשרת הכנסה, גישה והוצאה רק מסופה. שומרת על סדר ומאפשרת כפילויות • Graph - גרף: שומר קבוצת צמתים וקבוצת קשתות המחברות ביניהם • מתאים לבעיות הדורשות אבסטרקציה של רשתות כגון רשת כבישים, רשת מחשבים וכו'. מבוא לתכנות מערכות - 234122
התאמת מבנה נתונים לבעיה • לכל בעיה חשוב להתאים את מבנה הנתונים המתאים ביותר • התאמת מבנה הנתונים נעשית לפי שני שיקולים עיקריים: • איכות הקוד - בחירה טובה יוצרת קוד קצר יותר, פשוט יותר, מונעת שכפול קוד ומקשה על הכנסת באגים • למשל בחירת set במקום list מונעת הכנסת איבר פעמיים, חוסכת התעסקות בסדר הרשימה ובדיקות לפני הכנסת איבר בשנית • סיבוכיות - בחירת מבנה כך שהפעולות הקריטיות מהירות. שיקול זה לא יעניין אותנו בקורס זה ויילמד לעומק בקורס מבני נתונים • בבחירת המבנה כדאי להתחשב בדברים הבאים: • האם יש כפילויות? • האם צריך לשמור סדר שונה בכל פעם? • האם ניתן לקחת מבנה ספציפי יותר כך שייחסכו בדיקות מיותרות? מבוא לתכנות מערכות - 234122
בחירת מבני נתונים - סיכום • מבני הנתונים הנלמדים בקורס הם List, Set, Stack ו-Graph • יש לבחור מבנה נתונים מתאים לבעיה כדי להקל על העבודה מבוא לתכנות מערכות - 234122
שאלה לדוגמה - ADT מבוא לתכנות מערכות - 234122
שאלה לדוגמה • מבנה הנתונים ערמה מאפשר הכנסת איברים והוצאה של האיבר "המקסימלי" לפי סדר שהוגדר. כלומר הפעולות הנדרשות מערמה הן: • יצירת ערמה חדשה. • שחרור ערמה קיימת. • הכנסת איבר לערמה, ניתן להכניס מספר עותקים של אותו איבר. • הוצאת האיבר המקסימלי מהערמה. במקרה והערמה ריקה תוחזר שגיאה. א. כתבו את קובץ המנשק עבור ADT של ערמה ב. באילו מה-ADT שנלמדו בקורס כדאי להשתמש למימוש בערמה? מדוע? ג. כתבו את הקוד הדרוש למימוש ה-struct עבור הערמה ד. ממשו את הפונקציה עבור יצירת ערמה חדשה מבוא לתכנות מערכות - 234122
סעיף א' #ifndef _HEAP_H #define_HEAP_H #include<stdbool.h> typedefstructheap_t* Heap; typedefenum{ HEAP_SUCCESS,HEAP_NULL_ARGUMENT, HEAP_OUT_OF_MEMORY, HEAP_EMPTY } HeapResult; HeapheapCreate(void* (*copy)(void*), void(*release)(void*), bool(*compare)(void*,void*)); HeapResultheapPush(Heapheap, void* element); HeapResultheapPop(Heapheap, void** element); voidheapDestroy(Heap heap); #endif ניתן להגדיר את המצביעים ישירות או להוסיף typedef מתאימים מבוא לתכנות מערכות - 234122
סעיפים ב' ו-ג' • נבחר להשתמש ב-List עבור מימוש הערמה: • ייתכנו העתקים של אותו איבר בערמה • יהיה לנו נוח יותר להוציא את האיבר ששמור בראש הרשימה • מימוש המבנה בקובץ ה-C: structheap_t { List items; bool (*compare)(void*,void*); }; איפה יישמרו המצביעים לשאר לפונקציות? מבוא לתכנות מערכות - 234122
סעיף ד' • HeapheapCreate(void* (*copy)(void*), void (*release)(void*), bool(*compare)(void*,void*)) { • if (!copy || !release || !compare) { • return NULL; • } • Heapheap = malloc(sizeof(*heap)); • if (!heap) { • return NULL; • } • heap->items = listCreate(copy, release); • if (!heap->items) { • heapDestroy(heap); • return NULL; • } • heap->compare = compare; • return heap; • } מבוא לתכנות מערכות - 234122