100% found this document useful (1 vote)
2K views35 pages

חוברת תרגול

Uploaded by

justme85
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
2K views35 pages

חוברת תרגול

Uploaded by

justme85
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 35

‫תכנות מתקדם בשפת ‪C‬‬

‫חוברת תרגילים‬

‫‪-1-‬‬
‫קלט פלט‬

‫שאלה ‪1‬‬
‫א‪ .‬כתבו תוכנית המדפיסה לוח כפל בגודל ‪. 10 ×10‬‬
‫ב‪ .‬כתבו תוכנית המקבלת מספר‪ ,MaxMult ,‬ומדפיסה לוח כפל בגודל ‪. MaxMult × MaxMult‬‬

‫לדוגמא עבור‪ MaxMult=4 :‬יודפס‪:‬‬

‫‪1‬‬ ‫‪2 3 4‬‬


‫‪2‬‬ ‫‪4 6 8‬‬
‫‪3‬‬ ‫‪6 9 12‬‬
‫‪4‬‬ ‫‪8 12 16‬‬

‫הנחיות לשני הסעיפים‬


‫ההדפסות צריכות להיות בצורת טבלה ועל כל שדה בטבלה להיות מיושר לימין )כך שבכל טור ספרת‬
‫האחדות תהיינה אחת מעל השנייה‪ ,‬ספרת העשרות תהיינה אחת מעל השנייה‪ ,‬וכך הלאה עבור כל‬
‫הספרות(‬
‫הקפידו לא להכניס רווחים מיותרים )לפני המספר שבפינה הימנית תחתונה צריך להופיע רווח אחד‬
‫בדיוק(‪.‬‬

‫שאלה ‪2‬‬
‫כתבו את הפונקציה‪:‬‬
‫)‪void printFormattedIntegers(char* format, char* numbers‬‬

‫הפונקציה מקבלת כקלט מחרוזת‪ ,format ,‬המכילה פורמט להדפסה של מספרים וכן מחרוזת‪,‬‬
‫‪ ,numbers‬המכילה מספרים שלמים בבסיס דצימלי מופרדים במספר כלשהו של רווחים לבנים‪.‬‬
‫על הפונקציה להדפיס את המספרים מהמחרוזת ‪ ,numbers‬בהתאם לתווי ההסבה שב‪ .format-‬כך‬
‫שתו ההסבה הראשון ב‪ format-‬יקבע כיצד ייוצג המספר הראשון ב‪ ,numbers-‬תו ההסבה השני‬
‫יקבע כיצד ייוצג המספר השני וכך הלאה )בדומה ל‪.(printf-‬‬

‫מחרוזת הפורמט יכולה להכיל את תווי ההסבה הבאים )כמו ב‪:(printf -‬‬
‫‪ – %d‬ממיר ‪ int‬לטקסט המכיל ייצוג עשרוני של המספר‬
‫‪ – %x‬ממיר ‪ int‬לטקסט המכיל ייצוג הקסהדצימלי )בסיס ‪ (16‬של המספר‬
‫‪ – %o‬ממיר ‪ int‬לטקסט המכיל ייצוג אוקטלי )בסיס ‪ (8‬של המספר‬

‫וכן מחרוזת הפורמט יכולה להכיל את תווי ההסבה הבאים אשר אינם חוקיים ב‪:printf -‬‬
‫‪ – %b‬ממיר ‪ int‬לטקסט המכיל ייצוג בינארי )בסיס ‪ (2‬של המספר‬
‫‪ – %r‬ממיר ‪ int‬לטקסט המכיל ייצוג רומי של המספר‬

‫כמו כן מחרוזת הפורמט יכולה להכיל תווים שאינם תווי הסבה‪ ,‬אשר מודפסים כמו שהם‪.‬‬

‫‪-2-‬‬
‫דוגמאות‪:‬‬
‫הקריאה‪:‬‬
‫‪printFormattedIntegers("Dec: %d Hex: %x Roman: %r"," 123‬‬ ‫‪10‬‬
‫)"‪9‬‬
‫תגרום להדפסת הפלט‪:‬‬
‫‪Dec: 123 Hex: A Roman: IX‬‬

‫הקריאה‪:‬‬
‫)"‪printFormattedIntegers("%b in Binary is %o in Octal" ,"18 18‬‬
‫תגרום להדפסת הפלט‪:‬‬
‫‪10010 in Binary is 22 in Octal‬‬

‫הנחיות‪:‬‬
‫היעזרו בוויקיפדיה כדי למצוא את התיאור המלא של שיטת הספירה הרומית‪:‬‬ ‫‪.1‬‬
‫‪ [Link] את הערך‪":‬ספרות רומיות"‬
‫הניחו כי הקלט לפונקציה תקין‪ ,‬כלומר הניחו כי המחרוזת ‪ numbers‬אכן מכילה מספרים‬ ‫‪.2‬‬
‫שלמים מיוצגים בבסיס ‪ 10‬ומופרדים ברווח לבן וכי ‪ format‬מכיל מחרוזת פורמט חוקית וכן‬
‫שמספר תווי ההסבה במחרוזת הפורמט מתאים למספר המספרים שהתקבלו במחרוזת‬
‫‪.numbers‬‬
‫השתמשו ככל הניתן בפונקציות הקלט‪/‬פלט שנלמדו בשיעור ‪printf ,scanf ,sscanf ,sprintf‬‬ ‫‪.3‬‬
‫וכו'‪.‬‬
‫ניתן להיעזר בפונקציה ‪) strtok‬חפשו תיעוד המסביר כיצד להשתמש בה( אך בהחלט ניתן‬ ‫‪.4‬‬
‫להסתדר גם בלעדיה‪.‬‬
‫הגדירו פונקציות עזר וחלקו את הבעיה לתת בעיות‪.‬‬ ‫‪.5‬‬

‫‪-3-‬‬
‫מצביעים והקצאות דינאמיות‬

‫שאלה ‪1‬‬
‫כתבו את הפונקציה‪:‬‬ ‫א‪.‬‬
‫;)‪int** pointerSort(int* arr, int size‬‬

‫הפונקציה מקבלת כקלט מערך של מספרים שלמים‪ ,arr ,‬ואת גודלו‪.size ,‬‬
‫על הפונקציה להחזיר מערך של מצביעים‪ ,‬כך שהמצביעים במערך יצביעו על איברי ‪arr‬‬
‫בצורה ממוינת מהקטן לגדול‪.‬‬
‫כלומר‪ ,‬המצביע בתא ה‪ 0-‬של המערך המוחזר מצביע לאיבר המינימלי‪.‬‬

‫הערה‪ :‬ממשו את המיון בשיטת ‪.merge-sort‬‬

‫= ‪arr‬‬ ‫לדוגמא‪ :‬אם המערך ‪ arr‬הוא‬

‫אז המערך המוחזר יהיה‪:‬‬

‫כתבו גרסא נוספת לפונקציה‪ .‬הפעם כותרת הפונקציה תהיה‪:‬‬ ‫ב‪.‬‬


‫;)‪void pointerSort(int* arr, int size, int*** pointers‬‬
‫הפונקציה מקבלת מערך של מספרים שלמים‪ ,arr ,‬ואת גודלו‪ .size ,‬הפעם על הפונקציה‬
‫להחזיר כפרמטר פלט את מערך המצביעים‪.‬‬

‫שאלה ‪2‬‬
‫בהינתן פולינום עם מקדמים שלמים מהצורה‪:‬‬
‫‪Cn X n + Cn −1 X n −1 + ... + C1 X 1 + C0‬‬
‫כל איבר בפולינום נקרא מונום‪ .‬נגדיר רשומה לייצוג מונום כנ"ל באופן הבא‪:‬‬
‫{‪typedef struct monom‬‬
‫;‪int coefficient‬‬ ‫המקדם‪//‬‬
‫;‪int power‬‬ ‫החזקה‪//‬‬
‫;‪}Monom‬‬
‫מבנה הנתונים לייצוג פולינומים‪ :‬מערך של רשומות מטיפוס ‪ ,Monom‬המקיים‪:‬‬
‫• המערך לא יכיל מונום שמקדמו ‪.0‬‬
‫• המונומים במערך יופיעו בסדר חזקות יורד ממש )כלומר המונום עם החזקה הגדולה ביותר יופיע‬
‫בתא הראשון במערך(‪.‬‬

‫כתבו תוכנית הקולטת מהמשתמש שני פולינומים‪ ,‬ומדפיסה את פולינום הסכום שלהם ואת פולינום‬
‫המכפלה שלהם‪ ,‬כל אחד בשורה נפרדת‪.‬‬
‫הערות‪:‬‬
‫‪ .1‬כל פולינום בקלט יהיה שורה באורך לא ידוע של זוגות )מקדם וחזקה(‪.‬‬
‫כך למשל השורה‪:‬‬
‫‪2 4 -5 1 0 6 6 4 -8 0 7 3‬‬
‫מייצגת את הפולינום‪. 8 x 4 + 7 x 3 − 5 x − 8 :‬‬
‫‪ .2‬הדפסת פולינום צריכה להיות בסדר חזקות יורד‪ ,‬ללא מקדמים שהם ‪.0‬‬
‫למשל את הפולינום‪ 8 x 4 + 7 x 3 − 5 x − 8 :‬יש להדפיס‪.8x^4 + 7x^3 - 5x - 8 :‬‬

‫‪-4-‬‬
‫שאלה ‪3‬‬
‫כתוב תוכנית הקולטת שורות טקסט מהמשתמש ואוגרת אותן במערך של מחרוזות‪.‬‬
‫התוכנית תקלוט בהתחלה מספר הקובע את גודל המאגר‪ ,‬ולאחר מכן תקלוט שורות אותן תכניס למאגר‪.‬‬
‫במידה והמאגר מתמלא‪ ,‬יש "לזרוק" את השורה הותיקה ביותר‪.‬‬
‫בנוסף לתחזוקה של מבנה הנתונים המתואר לעיל‪ ,‬על התוכנית לאפשר למשתמש לשחזר שורות מתוך‬
‫המאגר‪ .‬פקודות השחזור ניתנות כאן‪:‬‬
‫משחזרת את השורה האחרונה שהוכנסה למאגר‪.‬‬ ‫!!‬
‫משחזרת את שורה מספר ‪ .n‬במידה ואין כזו ‪ -‬תודפס הודעת שגיאה‪.‬‬ ‫‪!n‬‬
‫)מספרי השורות ניתנים באופן רציף מתחילת התוכנית ועד סיומה(‬
‫משחזרת את השורה האחרונה שהתחילה ב‪ str -‬מהמאגר‪ .‬במידה ואין‬ ‫‪!str‬‬
‫כזו‪ ,‬תודפס הודעת שגיאה‪.‬‬
‫מדפיסה את תוכן המאגר‪ .‬כל שורה תופיע אחרי מספר השורה‪.‬‬ ‫‪!print‬‬
‫)מספרי השורות ניתנים באופן רציף מתחילת התוכנית ועד סיומה(‬
‫יציאה‪.‬‬ ‫‪!quit‬‬

‫שחזור שורה פירושו להדפיס את השורה המשוחזרת‪.‬‬


‫אם השחזור הצליח‪ ,‬יש להכניס למאגר את השורה המשוחזרת כולה‪ .‬אחרת‪ ,‬אין להכניס דבר למאגר‪.‬‬
‫דוגמת הרצה‬
‫)הקלט מהמשתמש מופיע בפונט מודגש ונטוי והפלט שהתוכנית מדפיסה מופיע בפונט רגיל(‬
‫‪Please enter the history storage size‬‬
‫‪>5‬‬
‫‪The storage size was set to 5‬‬
‫‪> some text‬‬
‫‪> some more text‬‬
‫‪> and more text‬‬
‫‪> !2‬‬
‫‪some more text‬‬
‫‪> hello world‬‬
‫!! >‬
‫‪hello world‬‬
‫!! >‬
‫‪hello world‬‬
‫‪> !2‬‬
‫‪Could not restore line number 2, currently lines 3-7 are‬‬
‫‪in the storage‬‬
‫‪> !print‬‬
‫‪3. and more text‬‬
‫‪4. some more text‬‬
‫‪5. hello world‬‬
‫‪6. hello world‬‬
‫‪7. hello world‬‬
‫‪> !some‬‬
‫‪some more text‬‬
‫‪> !quit‬‬
‫‪Thanks for using the history application, bye bye‬‬
‫הערות‬
‫‪ .1‬לא ניתן להניח אורך שורה מקסימאלי‬
‫‪ .2‬ניתן להניח כי כל שורה שאינה פקודת שחזור תתחיל באות אנגלית‬
‫‪ .3‬פתרון יעיל הוא פתרון אשר פעולת הכנסה למאגר תתבצע בזמן )‪O(1‬‬

‫‪-5-‬‬
‫שאלה ‪4‬‬
‫כתוב פונקציה המקבלת שני פרמטרים שהם שתי מחרוזות‪.‬‬
‫הפונקציה מוציאה מן המחרוזת הראשונה את כל התווים המופיעים במחרוזת השנייה‪.‬‬
‫הפונקציה מחזירה מחרוזת חדשה המכילה את הרצף המכווץ‪.‬‬

‫לדוגמא‪:‬‬
‫)”‪ strsqz ("hello world", “l‬תוציא "‪“heo word‬‬

‫אם הפונקציה הופעלה בשנית‪ ,‬עם פרמטר ראשון שהוא המחרוזת הריקה )"" או ‪ ,(NULL‬יש לבצע את‬
‫הכיווץ על תוצאת ההפעלה הקודמת של הפונקציה‪.‬‬

‫לדוגמא‪:‬‬
‫)"‪ strsqz (NULL, "or‬תוציא "‪.“he wd‬‬

‫‪-6-‬‬
‫רשימות מקושרות‬
‫שאלה ‪1‬‬
‫נתונים המבנים הבאים לייצוג רשימה מקושרת של תווים‪:‬‬
‫‪typedef struct list_node‬‬
‫{‬
‫;‪char* dataPtr‬‬
‫;‪struct list_node* next‬‬
‫;‪}ListNode‬‬

‫‪typedef struct list‬‬


‫{‬
‫;‪ListNode* head‬‬
‫;‪ListNode* tail‬‬
‫;‪}List‬‬

‫נתונה רשימה מקושרת שנתוניה הם ספרות‪ ,‬ואותיות אנגליות קטנות בלבד‪ .‬הרשימה המקושרת‬
‫במקורה הכילה את המידע הבא על תלמיד‪ :‬שמו הפרטי של התלמיד )המופיע באותיות קטנות( אות אחר‬
‫אות ברשימה‪ ,‬ואחריו מופיע ציונו הממוצע )הניתן בספרות המייצגות מספר חיובי שלם( סיפרה אחר‬
‫סיפרה ברשימה‪ .‬לרוע המזל‪ ,‬כתוצאה מבאג במחשב‪ ,‬נשזרו התווים אלו באלו‪ ,‬והתקבלה רשימה‬
‫מעורבלת‪.‬‬

‫דוגמה‪ :‬רשימה שבמקור הכילה את התווים ‪ muℓℓy94‬כאשר ‪ m‬היה הנתון בצומת הראשון‪ u ,‬היה‬
‫הנתון בצומת השני‪ ,‬וכ"ו )כלומר שמו הפרטי הוא ‪ ,muℓℓy‬וציונו ‪ ,(94‬שונתה וכעת היא מכילה את‬
‫התווים‪) m9uℓ4ℓy :‬כאשר ‪ m‬הוא הנתון בצומת הראשון‪ 9 ,‬הוא הנתון בצומת השני‪ ,‬וכ"ו(‪.‬‬

‫שימו לב כי סדר התווים והספרות המקורי נשמר בתוך הערבול‪) .‬לדוגמא‪ u ,‬יבוא אחרי ‪ ℓ ,m‬יבוא אחרי‬
‫‪ ,u‬וכו'(‪.‬‬
‫כתבו פונקציה המקבלת רשימה מעורבלת ומחזירה מבנה לייצוג תלמיד )שיוגדר להלן( המכיל את שמו‬
‫הפרטי ואת ציונו הממוצע של התלמיד המתקבלים מהפרדת המילים ברשימה המעורבלת‪.‬‬

‫ה – ‪ prototype‬של הפונקציה הוא‪:‬‬


‫‪Student unScramble (List‬‬ ‫;)‪lst‬‬
‫כאשר מבנה של תלמיד מוגדר להלן‪:‬‬

‫‪typedef struct student‬‬


‫{‬
‫;‪List first‬‬
‫;‪int grade‬‬
‫;‪} Student‬‬
‫השדה ‪ first‬הוא רשימה המייצגת את שמו הפרטי של הסטודנט אות אחר אות‪.‬‬ ‫•‬
‫והשדה ‪ grade‬הוא מספר המייצג את ממוצע ציוניו של הסטודנט‪.‬‬ ‫•‬

‫הערות‪:‬‬
‫• אין להשתמש בהקצאה דינאמית‪ ,‬אלא לשנות מצביעים ברשימת הקלט‪.‬‬

‫‪-7-‬‬
‫שאלה ‪2‬‬
‫נתונה ההגדרה הבאה לייצוג רשימה מקושרת של מספרים שלמים‪:‬‬
‫{‪typedef struct listNode‬‬
‫;‪int* dataPtr‬‬
‫;‪struct listNode* next‬‬
‫;‪}ListNode‬‬

‫‪typedef struct list‬‬


‫{‬
‫;‪ListNode* head‬‬
‫;‪ListNode* tail‬‬
‫;‪}List‬‬

‫כתבו את הפונקציה הבאה‪ ,‬בארבע גרסאות‪:‬‬


‫;)‪List merge(List lst1, List lst2‬‬

‫הפונקציה מקבלת כקלט ‪ lst1‬ו‪ ,lst2-‬שתי רשימות מקושרות ממוינות בסדר עולה של ערכים‬
‫עליהם מצביעים השדות ‪.dataPtr‬‬
‫על הפונקציה למזג את שתי הרשימות לרשימה ממוינת אחת המכילה את כל האיברים של רשימות הקלט‪.‬‬
‫על הפונקציה להחזיר את הרשימה הממוזגת‪.‬‬

‫בגרסא זו‪ ,‬הפונקציה איננה רקורסיבית ועליה ליצור רשימה חדשה‪.‬‬ ‫א‪.‬‬
‫הפונקציה לא תשנה הצבעות ברשימות ‪ lst1‬ו‪) lst2 -‬בסוף ריצת הפונקציה שתי הרשימות‬
‫נותרות ללא שינוי(‪.‬‬

‫בגרסא זו‪ ,‬הפונקציה איננה רקורסיבית ועליה למזג את שתי הרשימות לרשימה אחת ממוינת ע"י‬ ‫ב‪.‬‬
‫שינוי מצביעים בלבד )ללא שימוש בהקצאות חדשות(‪.‬‬

‫בגרסא זו‪ ,‬הפונקציה רקורסיבית ועליה ליצור רשימה חדשה‪.‬‬ ‫ג‪.‬‬

‫בגרסא זו‪ ,‬הפונקציה רקורסיבית ועליה למזג את שתי הרשימות ע"י שינוי מצביעים‪.‬‬ ‫ד‪.‬‬

‫הערה‪ :‬בסעיפים ג' ו‪ -‬ד' הפונקציה אינה חייבת להיות רקורסיבית בעצמה‪ ,‬היא יכולה להיות פונקציה‬
‫העוטפת פונקציה רקורסיבית )פונקציה אשר מפעילה לפונקציה רקורסיבית(‪.‬‬

‫‪-8-‬‬
‫שאלה ‪3‬‬
‫נתונה ההגדרה הבאה לייצוג רשימה מקושרת של מספרים שלמים‪:‬‬
‫{‪typedef struct listNode‬‬
‫;‪int* dataPtr‬‬
‫;‪struct listNode* next‬‬
‫;‪} ListNode‬‬

‫‪typedef struct list‬‬


‫{‬
‫;‪ListNode* head‬‬
‫;‪ListNode* tail‬‬
‫;‪} List‬‬

‫ניתן לייצג מספר טבעי גדול בעזרת רשימה של ספרותיו לפי הסדר כאשר הצומת הראשון ברשימה מכיל‬
‫מצביע לספרה הימנית והצומת האחרון מכיל מצביע לספרה השמאלית‪.‬‬
‫למשל את המספר ‪ 197‬נייצג ברשימה‪:‬‬

‫‪7‬‬ ‫‪9‬‬ ‫‪1‬‬


‫;)‪void printNumber(List num‬‬ ‫א‪ .‬כתבו את הפונקציה‪:‬‬
‫שמקבלת מספר המיוצג ברשימה כפי שתואר‪ ,‬ומדפיסה אותו‪) .‬הפונקציה יכולה להיות רקורסיבית או‬
‫איטרטיבית לפי בחירתכם(‪.‬‬

‫;)‪void addNumbers(List n1, List n2, List* sum‬‬ ‫ב‪ .‬כתבו את הפונקציה‪:‬‬
‫שמקבלת ‪ 2‬רשימות שמייצגות שני מספרים ומצביע לרשימה שלישית ומעדכנת את ‪ sum‬להצביע על‬
‫רשימה השלישית המייצגת את סכום ‪ 2‬המספרים‪.‬‬

‫;)‪void multNumbers(List n1, List n2 , List* prod‬‬ ‫ג‪ .‬כתבו את הפונקציה‪:‬‬


‫שמקבלת ‪ 2‬רשימות שמייצגות שני מספרים ומצביע לרשימה שלישית‪ ,‬ומעדכנת את ‪ prod‬להצביע‬
‫על רשימה השלישית המייצגת את מכפלת ‪ 2‬המספרים‪.‬‬

‫הערה‪ :‬בסעיפים ב' ו‪ -‬ג' אין להשתמש באלגוריתם הממיר את המספרים המיוצגים ברשימות למשתנים‬
‫מטיפוס ‪ int‬או ‪ double‬ולבצע את החיבור והכפל עליהם )גישה כזו מחמיצה את המטרה של ייצוג‬
‫המספרים ברשימה(‪.‬‬

‫‪-9-‬‬
‫שאלה ‪4‬‬
‫נתון המבנה הבא לייצוג רשימה מקושרת דו‪-‬כוונית‪:‬‬
‫{‪typedef struct dListNode‬‬
‫;‪int* dataPtr‬‬
‫;‪struct dListNode* next‬‬
‫;‪struct dListNode* prev‬‬
‫;‪}DListNode‬‬

‫‪typedef struct dList‬‬


‫{‬
‫;‪DListNode* head‬‬
‫;‪DListNode* tail‬‬
‫;‪}DList‬‬

‫כתבו את הפונקציה‪:‬‬
‫;)‪void removeDuplicates(DList* lst‬‬
‫הפונקציה מקבלת מצביע לרשימה דו‪-‬כוונית של ציוני מבחן פסיכומטרי‪ ,‬בה כל צומת מכיל מצביע לציון‬
‫של מבחן )כל ציון הינו מספר שלם בין ‪ 200‬ל‪ .(800 -‬כל ציון יכול להופיע יותר מפעם אחת‪ .‬על‬
‫הפונקציה לשנות את הרשימה המקורית כך שכל ציון יופיע בה פעם אחת בלבד‪ .‬על הפונקציה לעדכן את‬
‫‪ lst‬להכיל את הרשימה המצומצמת ולשחרר את הזיכרון של הצמתים שהוסרו‪.‬‬

‫הערה‪ :‬ממשו פתרון יעיל ככל הניתן‪.‬‬

‫‪- 10 -‬‬
‫שאלה ‪5‬‬
‫נתונות ההגדרות הבאות‪:‬‬
‫{‪typedef struct letterEncrypt‬‬
‫;‪char letter‬‬
‫;‪int count‬‬
‫;‪}LetterEncrypt‬‬

‫{‪typedef struct listNode‬‬


‫;‪LetterEncrypt* sequencePtr‬‬
‫;‪struct listNode* next‬‬
‫;‪}ListNode‬‬

‫‪typedef struct list‬‬


‫{‬
‫;‪ListNode* head‬‬
‫;‪ListNode* tail‬‬
‫;‪}List‬‬

‫רוצים לקודד )‪ (encode‬מחרוזת של אותיות ‪) lower case‬קטנות(‪ ,‬ברשימה של נתונים מהטיפוס‬


‫‪.LetterEncrypt‬‬
‫כל רצף של אותיות זהות סמוכות‪ ,‬נייצג ברשומה אחת‪ ,‬מסוג ‪ ,LetterEncrypt‬שתכיל בשדה‬
‫‪ letter‬את האות ובשדה ‪ count‬את אורך הרצף‪.‬‬
‫למשל את הרצף ”‪ “eeee‬נייצג ברשומה שבה‪ , letter=’e’ :‬ו‪.count=4 -‬‬
‫בצורה זו ניתן לקודד מחרוזות שלמות‪ :‬עבור כל רצף באורך מקסימאלי של אותיות זהות סמוכות‬
‫במחרוזת המקורית‪ ,‬תופיע רשומה אחת שתייצג את הקידוד של רצף זה‪ .‬את סידרת הרשומות נשמור‬
‫ברשימה של רשומות‪.‬‬

‫לדוגמא‪ ,‬הרשימה הבאה מייצגת קידוד של המחרוזת “ ‪:"aaaabbaaac‬‬

‫‪a‬‬ ‫‪b‬‬ ‫‪a‬‬ ‫‪c‬‬


‫‪4‬‬ ‫‪2‬‬ ‫‪3‬‬ ‫‪1‬‬

‫כתבו פונקציה‬ ‫א‪.‬‬


‫;)‪List encode(char* str‬‬
‫המקבלת כקלט מחרוזת ‪ .str‬על הפונקציה להחזיר רשימה של רשומות מטיפוס‬
‫‪ .LetterEncrypt‬רשימה זו מייצגת את הקידוד של המחרוזת ‪.str‬‬

‫כתבו את הפונקציה‪:‬‬ ‫ב‪.‬‬


‫;)‪int encodedStrcmp(List str1, List str2‬‬
‫אשר מקבלת שתי מחרוזות בייצוג מקודד‪ ,‬ומשווה ביניהן לקסיקוגרפית )סדר מילוני( ‪ ,‬כמו ש‪-‬‬
‫‪ strcmp‬משווה בין שתי מחרוזות רגילות‪.‬‬
‫על הפונקציה להחזיר‪:‬‬
‫• מספר שלילי אם ‪ str1‬קטנה לקסיקוגרפית מ‪.str2 -‬‬
‫• מספר חיובי אם ‪ str1‬גדולה לקסיקוגרפית מ‪.str2 -‬‬
‫• אפס אם הן שוות‪.‬‬

‫‪- 11 -‬‬
‫עצים בינאריים‬

‫שאלה ‪1‬‬
‫עץ בינארי מוגדר כך‪:‬‬
‫{‪typedef struct treeNode‬‬
‫;‪int data‬‬
‫;‪struct treeNode* left‬‬
‫;‪struct treeNode* right‬‬
‫;‪} TreeNode‬‬

‫{‪typedef struct tree‬‬


‫;‪TreeNode* root‬‬
‫;‪} Tree‬‬

‫כתבו את פונקציה‪:‬‬
‫;)‪float averageOfValues(Tree tr‬‬

‫המקבלת עץ בינארי‪ ,‬ומחזירה את הממוצע של כל הערכים הנמצאים בצמתי העץ‪.‬‬


‫על הפונקציה לרוץ ביעילות )‪ , O ( n‬כאשר ‪ n‬הוא מספר הצמתים הכולל בעץ‪.‬‬

‫למשל עבור העץ‪:‬‬

‫‪3‬‬

‫‪2‬‬ ‫‪5‬‬

‫‪7‬‬ ‫‪9‬‬

‫‪1‬‬

‫הפונקציה תחזיר ‪) 4.5‬כי‪.( 2+3+1+69+7+5 = 4 12 :‬‬

‫הערה‪ :‬אין להשתמש במשתנים סטאטיים‪.‬‬

‫‪- 12 -‬‬
‫שאלה ‪2‬‬
‫בהינתן ההגדרה הבאה למבנה עבור עץ בינארי‪:‬‬
‫{ ‪typedef struct treeNode‬‬
‫;‪int data‬‬
‫;‪struct treeNode* left‬‬
‫;‪struct treeNode* right‬‬
‫;‪}TreeNode‬‬

‫{‪typedef struct tree‬‬


‫;‪TreeNode* root‬‬
‫;‪} Tree‬‬

‫אם ‪ T‬הוא עץ בינארי נגדיר כי ‪" T‬מאוזן עפ"י גובה" כאשר לכל צומת‪ ,‬גובה תת העץ השמאלי וגובה‬
‫תת העץ הימני שווים או שהפרשם לא גדול מ ‪.1 -‬‬

‫למשל העץ הבא מאוזן עפ"י גובה‪:‬‬

‫‪3‬‬

‫‪9‬‬ ‫‪6‬‬

‫‪2‬‬ ‫‪1‬‬ ‫‪4‬‬

‫‪1‬‬ ‫‪1‬‬

‫כתבו פונקציה ‪ isHeightBalanced‬המקבלת עץ בינארי ומחזירה ‪ true‬אם"ם העץ מאוזן לפי‬


‫גובה‪.‬‬

‫הערות‪:‬‬
‫‪ .1‬אין להשתמש במשתנים סטטיים‪.‬‬
‫‪ .2‬גובה של עץ עם צומת יחיד מוגדר להיות ‪.0‬‬
‫‪ .3‬עץ ריק הוא מאוזן לפי גובה‪.‬‬

‫‪- 13 -‬‬
‫שאלה ‪3‬‬
‫נתונים המבנים הבאים עבור עץ בינארי ועבור רשימה מקושרת‪:‬‬

‫{ ‪typedef struct treeNode‬‬


‫;‪int data‬‬
‫;‪struct treeNode* left‬‬
‫;‪struct treeNode* right‬‬
‫;‪} TreeNode‬‬

‫{‪typedef struct tree‬‬


‫;‪TreeNode* root‬‬
‫;‪} Tree‬‬

‫{ ‪typedef struct listNode‬‬


‫;‪int data‬‬
‫;‪struct listNode* next‬‬
‫;‪} ListNode‬‬

‫{ ‪typedef struct list‬‬


‫;‪ListNode* head‬‬
‫;‪ListNode* tail‬‬
‫;‪} List‬‬

‫כתבו את הפונקציה הבאה‪:‬‬

‫;)‪int leavesList (Tree tr , List* resLst‬‬

‫הפונקציה מקבלת עץ בינארי‪ ,‬כפרמטר קלט‪ ,‬ומצביע לרשימה ‪,‬כפרמטר פלט‪.‬‬


‫על הפונקציה להחזיר בפרמטר הפלט את רשימת נתוני העלים של העץ‪ .‬סדר אברי הרשימה יהיה כסדר‬
‫העלים מימין לשמאל‪ .‬כמו כן‪ ,‬על הפונקציה להחזיר‪ ,‬את סכום ערכי העלים בעץ‪ ,‬כערך מוחזר‪.‬‬

‫לדוגמא עבור העץ‪:‬‬

‫‪3‬‬

‫‪2‬‬ ‫‪5‬‬

‫‪4‬‬ ‫‪7‬‬ ‫‪9‬‬

‫‪1‬‬

‫הפונקציה תיצור את הרשימה‪ 1Æ7Æ4 :‬ותחזיר את הערך ‪.12‬‬

‫הערה‪ :‬אין להשתמש במשתנים סטאטיים‪.‬‬

‫‪- 14 -‬‬
‫שאלה ‪4‬‬
‫קוטר של עץ בינארי מוגדר להיות מספר הקשתות במסלול הארוך ביותר בין כל שני צמתים בעץ‪.‬‬
‫למשל‪ ,‬בעץ הנ"ל המסלול המודגש הוא המסלול הארוך ביותר בין שני צמתים בעץ‪ ,‬ולכן קוטר העץ הנ"ל‬
‫הוא ‪.6‬‬

‫עץ בינארי מוגדר כך‪:‬‬


‫{‪typedef struct treeNode‬‬
‫;‪int data‬‬
‫;‪struct treeNode* left‬‬
‫;‪struct treeNode* right‬‬
‫;‪} TreeNode‬‬

‫{‪typedef struct tree‬‬


‫;‪TreeNode* root‬‬
‫;‪} Tree‬‬

‫כתבו את פונקציה‪:‬‬
‫;)‪int treeDiameter(Tree tr‬‬

‫הפונקציה מקבלת כקלט עץ בינארי‪ ,‬ומחזירה את הקוטר של העץ‪.‬‬


‫על הפונקציה לרוץ ביעילות )‪ , Θ(n‬כאשר ‪ n‬הוא מספר הצמתים הכולל בעץ‪.‬‬

‫הערה‪ :‬אין להשתמש במשתנים סטאטיים‪.‬‬

‫‪- 15 -‬‬
‫שאלה ‪5‬‬
‫עץ בינארי מוגדר כך‪:‬‬
‫{‪typedef struct treeNode‬‬
‫;‪int data‬‬
‫;‪struct treeNode* left‬‬
‫;‪struct treeNode* right‬‬
‫;‪} TreeNode‬‬

‫{‪typedef struct tree‬‬


‫;‪TreeNode* root‬‬
‫;‪} Tree‬‬

‫כתבו את פונקציה‪:‬‬
‫;)‪void printByLevels(Tree tr‬‬

‫המדפיסה את הנתונים בעץ ‪ tr‬לפי רמות )קודם את הנתון ברמה ‪ ,0‬אחריו את נתונים ברמה ‪ ,1‬אחריהם‬
‫את הנתונים ברמה ‪ ,2‬וכך הלאה(‪ ,‬כל רמה תודפס משמאל לימין‪.‬‬
‫על הפונקציה לרוץ ביעילות )‪ , Θ(n‬כאשר ‪ n‬הוא מספר הצמתים הכולל בעץ‪.‬‬

‫למשל עבור העץ‪:‬‬

‫‪3‬‬

‫‪2‬‬ ‫‪5‬‬

‫‪4‬‬ ‫‪7‬‬ ‫‪9‬‬

‫‪1‬‬

‫יודפס‪3 2 5 4 7 9 1 :‬‬

‫הערות‪:‬‬
‫‪ .1‬בכדי לעמוד בדרישות היעילות ייתכן ותרצו להשתמש במבנה נתונים נוסף‪.‬‬
‫‪ .2‬אין להשתמש במשתנים סטאטיים‪.‬‬

‫‪- 16 -‬‬
‫קבצים‬
‫שאלה ‪:1‬‬
‫כתבו תוכנית המקבלת כפרמטר שם של קובץ טקסט‪ .‬הקובץ מכיל אינפורמאציה על מלאי של מוצרים‬
‫בחנות‪.‬‬
‫בשורה הראשונה יופיע מספר המייצג את מספר המוצרים במאגר‪.‬‬
‫בכל שורה החל מהשורה השנייה יופיעו הנתונים על מוצר אחד‪ .‬עבור כל מוצר במאגר יישמרו )משמאל‬
‫לימין בשורה(‪ :‬שם המוצר‪ ,‬מחיר של יחידה אחת‪ ,‬מספר היחידות הזמינות‪.‬‬
‫בין כל שדה בשורה יכולים להופיע מספר כלשהו של סימני ‪.tab‬‬
‫שימו לב‪ :‬שם של מוצר יכול להיות יותר ממילה אחת‪.‬‬

‫להלן דוגמא לקובץ עפ"י הפורמט המתואר‪:‬‬

‫‪4‬‬
‫‪iPod nano‬‬ ‫‪1000‬‬ ‫‪12‬‬
‫‪iRiver‬‬ ‫‪1199‬‬ ‫‪4‬‬
‫‪Sony play station 3 3499 30‬‬
‫‪Xbox‬‬ ‫‪1819 5‬‬

‫על התוכנית להמיר קובץ זה לקובץ בינארי הבנוי לפי הפורמט הבא‪:‬‬
‫• מספר מטיפוס ‪ – int‬המייצג את מספר המוצרים השונים במאגר‬
‫• עבור כל מוצר במאגר )שורה בקובץ הטקסט( ישמרו בקובץ הבינארי המיוצר‪ ,‬הנתונים‬
‫הבאים‪:‬‬
‫‪ .1‬מספר מטיפוס ‪ – int‬המייצג את מספר התווים בשם המוצר )ללא ה‪.('\0' -‬‬
‫‪ .2‬רצף )באורך זה( של ‪-char‬ים – המכיל את שם המוצר‪.‬‬
‫‪ .3‬מספר מטיפוס ‪ – int‬המייצג את מחירו של המוצר‪.‬‬
‫‪ .4‬מספר מטיפוס ‪ – int‬המייצג את מספר היחידות הזמינות מהמוצר‪.‬‬

‫שמו של הקובץ המיוצר הוא כשמו של קובץ הקלט‪ ,‬בתוספת "‪ ".bin‬בסוף‪.‬‬
‫למשל‪ :‬נניח ששם קובץ הקלט הוא "‪ "[Link]‬שם הקובץ שיווצר יהיה "‪."[Link]‬‬

‫‪- 17 -‬‬
‫שאלה ‪2‬‬
‫א‪ .‬בסעיף זה נרצה להמיר תמונה צבעונית לתמונה בגווני אפור‪.‬‬

‫תמונה צבעונית מורכבת מפיקסלים )נקודות( בצבעים שונים‪ .‬מספר הפיקסלים בתמונה )רזולוציה( הוא‬
‫רוחב התמונה כפול גובה התמונה‪.‬‬
‫כל פיקסל בתמונה מיוצג על ידי שלשת מספרים )‪ (R,G,B‬הקובעת את עוצמת האדום‪ ,‬הירוק והכחול‬
‫בהתאמה‪.‬‬
‫הערכים השונים אותם יכולים לקבל ‪ ,G,R‬ו‪ B-‬נקבעים בהתאם לעומק התמונה‪ .depth ,‬בתמונה בעומק‬
‫‪ x‬כל אחד מבין ‪ ,G,R‬ו‪ B-‬יכול לקבל ערך בין ‪ 0‬ל‪) x-‬וכך מספר הגוונים האפשריים בתמונה הוא ‪.( x 3‬‬

‫לדוגמא בתמונה צבעונית בעומק ‪ 255‬כל אחד מבין ‪ R,G,B‬הוא מספר בין ‪ 0‬ל‪ 255-‬כאשר ‪ 0‬מציין כי‬
‫הגוון אינו מופיע כלל‪ ,‬ו‪ 255-‬מציין כי הגוון מופיע במלואו‪ .‬כל ערך בין ‪ 0‬ל‪ 255-‬קובע עוצמה עבור‬
‫הגוון‪.‬‬
‫השלשה )‪ (255,0,0‬תציין את הצבע אדום )אדום מופיע במלואו‪ ,‬ירוק וכחול אינם מופיעים כלל(‬
‫השלשה )‪ (0,255,0‬תציין את הצבע ירוק )אדום וכחול אינם מופיעים כלל‪ ,‬ירוק מופיע במלואו(‬
‫השלשה )‪ (255,0,255‬תציין את הצבע סגול )אדום וכחול מופיעים במלואם‪ ,‬ירוק אינו מופיע כלל(‬
‫השלשה )‪ (255,255,255‬תציין את הצבע לבן )כל הצבעים מופיעים במלואם(‬
‫השלשה )‪ (0,0,0‬תציין את הצבע שחור )כל הצבעים אינם מופיעים כלל(‬
‫השלשה )‪ (230,160,15‬תציין גוון של הצבע כתום )בהתאם ליחס בין מרכיבי הצבע השונים(‬

‫תמונה בגווני אפור מורכבת מפיקסלים בגווני אפור שונים‪ .‬אלא שהפעם כל פיקסל בתמונה מיוצג על ידי‬
‫מספר בודד המציין את רמת האפור של הפיקסל‪ .‬גם כאן לתמונה יש עומק הקובע את מספר הגוונים‬
‫)הפעם גווני האפור( האפשריים בתמונה‪ .‬בדומה לתמונה צבעונית‪ ,‬בתמונה בעומק ‪ x‬פיקסל בעל ערך ‪0‬‬
‫הוא שחור‪ ,‬ופיקסל בעל ערך ‪ x‬הוא לבן‪ .‬כל ערך בטווח מייצג גוון אפור )וכך מספר הגוונים האפשריים‬
‫בתמונה הוא ‪.( x‬‬

‫לדוגמא בתמונה בגווני אפור בעומק ‪ 255‬כל פיקסל הוא מספר בין ‪ 0‬ו‪255 -‬‬
‫הפיקסל ‪ – 0‬שחור‬
‫הפיקסל ‪ – 255‬לבן‬
‫הפיקסל ‪ – 200‬אפור בהיר‬
‫הפיקסל ‪ – 50‬אפור כהה‬

‫אלגוריתם להמרת תמונה צבעונית לתמונה בגווני אפור ‪ -‬בכדי להמיר תמונה צבעונית לתמונה בגווני‬
‫אפור עלינו להחליט עבור כל פיקסל צבעוני באיזה פיקסל בגוון אפור הוא יוחלף‪ .‬האלגוריתם בו נשתמש‬
‫הוא כזה הלוקח את הממוצע של ערכי ‪ R,G‬ו‪ B-‬להיות ערך גוון האפור‪ .‬כלומר גוון האפור של פיקסל‬
‫‪r + g +b‬‬
‫צבעוני ‪ r1,g1,b1‬יקבע להיות‪ . 1 1 1 :‬שימו לב שהתמונה אותה מייצר האלגוריתם היא תמונה‬
‫‪3‬‬
‫בגווני אפור בעלת אותם המימדים ואותו העומק כמו התמונה הצבעונית‪.‬‬

‫עליכם לכתוב תוכנית המקבלת כפרמטר שם של קובץ תמונה צבעונית בפורמט ‪ PPM‬עם ‪Magic‬‬
‫‪ .P3 :Number‬התוכנית טוענת את התמונה לזיכרון‪ ,‬ממירה אותה לתמונה בגווני אפור לפי האלגוריתם‬
‫שתואר לעיל ומייצרת קובץ בפורמט ‪ PGM‬עם ‪ P2 :Magic Number‬מתאים לתמונה‪ .‬שם קובץ הפלט‬
‫ללא הסיומת יהיה זהה לשם קובץ הקלט )ללא הסיומת(‪ ,‬והסיומת של שם קובץ הפלט תהיה ‪.PGM‬‬

‫הערה‪ :‬הסבר על הפורמטים ‪ PPM‬ו‪ PGM -‬יובא בהמשך‪.‬‬

‫‪- 18 -‬‬
‫ב‪ .‬בסעיף זה נרצה להמיר תמונה בגווני אפור לתמונה בשחור לבן‪.‬‬

‫תמונה בשחור לבן היא מקרה פרטי של תמונה בגווני אפור‪ .‬תמונה בשחור לבן היא תמונה בגווני אפור‬
‫בעומק ‪ .1‬נשים לב שבמקרה זה ישנם שני ערכים אפשריים בלבד לכל פיקסל‪ 0 .‬המייצג את הצבע שחור‬
‫ו‪ 1-‬המייצג את הצבע לבן‪.‬‬

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

‫בשלב הראשון מחלקים את גווני האפור ל‪ k 2 -‬חלקים שווי גודל )פרט אולי לאחרון(‪ ,‬לכל חלק מתאימים‬
‫מספר בין ‪ 0‬ל‪ k 2 − 1 -‬לפי הסדר‬

‫לדוגמא תמונה מעומק ‪ 255‬ממופה עבור ‪ k = 3‬לתשעת החלקים‪:‬‬


‫‪0‬‬ ‫‪0-28‬‬
‫‪1‬‬ ‫‪29-57‬‬
‫‪2‬‬ ‫‪58-86‬‬
‫‪3‬‬ ‫‪87-115‬‬
‫‪4‬‬ ‫‪116-144‬‬
‫‪5‬‬ ‫‪145-173‬‬
‫‪6‬‬ ‫‪174-202‬‬
‫‪7‬‬ ‫‪203-231‬‬
‫‪8‬‬ ‫‪232-255‬‬

‫בשלב השני מתאימים לכל פיקסל בתמונת גווני האפור המקורית מספר בין ‪ 0‬ל‪ k 2 − 1 -‬לפי החלק אליו‬
‫הוא משתייך‪.‬‬

‫לדוגמא‪:‬‬
‫‪7‬‬ ‫‪3‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪2‬‬ ‫‪203‬‬ ‫‪112‬‬ ‫‪39‬‬ ‫‪52‬‬ ‫‪155‬‬ ‫‪58‬‬
‫‪2‬‬ ‫‪3‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪5‬‬ ‫‪0‬‬ ‫‪70‬‬ ‫‪95‬‬ ‫‪42‬‬ ‫‪154‬‬ ‫‪144‬‬ ‫‪0‬‬
‫‪4‬‬ ‫‪3‬‬ ‫‪2‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪1‬‬ ‫‪137‬‬ ‫‪108‬‬ ‫‪68‬‬ ‫‪40‬‬ ‫‪151‬‬ ‫‪31‬‬
‫‪5‬‬ ‫‪3‬‬ ‫‪7‬‬ ‫‪4‬‬ ‫‪7‬‬ ‫‪2‬‬ ‫‪160‬‬ ‫‪90‬‬ ‫‪202‬‬ ‫‪127‬‬ ‫‪221‬‬ ‫‪73‬‬
‫‪7‬‬ ‫‪7‬‬ ‫‪4‬‬ ‫‪6‬‬ ‫‪6‬‬ ‫‪0‬‬ ‫‪225‬‬ ‫‪214‬‬ ‫‪123‬‬ ‫‪177‬‬ ‫‪190‬‬ ‫‪4‬‬
‫‪5‬‬ ‫‪2‬‬ ‫‪6‬‬ ‫‪6‬‬ ‫‪4‬‬ ‫‪8‬‬ ‫‪172‬‬ ‫‪69‬‬ ‫‪177‬‬ ‫‪197‬‬ ‫‪139‬‬ ‫‪248‬‬
‫‪4‬‬ ‫‪7‬‬ ‫‪0‬‬ ‫‪6‬‬ ‫‪7‬‬ ‫‪1‬‬ ‫‪136‬‬ ‫‪216‬‬ ‫‪20‬‬ ‫‪176‬‬ ‫‪213‬‬ ‫‪41‬‬

‫‪ - B‬תמונה מעומק ‪ 8‬בגווני אפור )‪(k=3‬‬ ‫‪ - A‬תמונה מעומק ‪ 255‬בגווני אפור‬

‫‪- 19 -‬‬
‫בשלב השלישי נעזרים במטריצה מסיכה )‪ (mask‬בגודל ‪ k × k‬שבה מופיעים המספרים מ‪ 0 -‬עד‬
‫‪ . k 2 − 1‬את המטריצה "פורשים" על התמונה שהתקבלה מהשלב הראשון )‪ ,(B‬כלומר משכפלים ללא‬
‫חפיפות את המטריצה החל מהפינה השמאלית העליונה של התמונה לאורך ולרוחב‪.‬‬

‫לדוגמא פרישת מטריצת המסיכה הבאה‪:‬‬

‫‪2‬‬ ‫‪6‬‬ ‫‪4‬‬


‫‪5‬‬ ‫‪0‬‬ ‫‪1‬‬
‫‪8‬‬ ‫‪3‬‬ ‫‪7‬‬

‫על התמונה ‪ B‬תיתן‪:‬‬

‫‪7‬‬ ‫‪3‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪2‬‬


‫‪2‬‬ ‫‪3‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪5‬‬ ‫‪0‬‬
‫‪4‬‬ ‫‪3‬‬ ‫‪2‬‬ ‫‪1‬‬ ‫‪5‬‬ ‫‪1‬‬

‫‪5‬‬ ‫‪3‬‬ ‫‪7‬‬ ‫‪4‬‬ ‫‪7‬‬ ‫‪2‬‬


‫‪7‬‬ ‫‪7‬‬ ‫‪4‬‬ ‫‪6‬‬ ‫‪6‬‬ ‫‪0‬‬
‫‪5‬‬ ‫‪2‬‬ ‫‪6‬‬ ‫‪6‬‬ ‫‪4‬‬ ‫‪8‬‬
‫‪4‬‬ ‫‪7‬‬ ‫‪0‬‬ ‫‪6‬‬ ‫‪7‬‬ ‫‪1‬‬

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

‫ומכאן שבדוגמא הנ"ל נקבל את התמונה הבאה בשחור לבן‪:‬‬

‫‪1‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬


‫‪0‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪0‬‬
‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪0‬‬
‫‪1‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪0‬‬
‫‪1‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪0‬‬
‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪1‬‬
‫‪1‬‬ ‫‪1‬‬ ‫‪0‬‬ ‫‪1‬‬ ‫‪1‬‬ ‫‪0‬‬
‫‪ – C‬תוצאה‪ -‬תמונה מעומק ‪) 1‬שחור לבן(‬

‫‪- 20 -‬‬
‫עליכם לכתוב תוכנית המקבלת כפרמטר שם של קובץ תמונה בגווני אפור בפורמט ‪ PGM‬עם ‪Magic‬‬
‫‪ .P2 :Number‬התוכנית טוענת את התמונה לזיכרון‪ ,‬ממירה אותה לתמונה בשחור לבן לפי האלגוריתם‬
‫שתואר לעיל ומייצרת קובץ )גם הוא בפורמט ‪ PGM‬עם ‪ (P2 :Magic Number‬מתאים לתמונה‪ .‬שם‬
‫קובץ הפלט ללא הסיומת יהיה זהה לשם קובץ הקלט )ללא הסיומת( אך בתוספת "‪ ,"bw‬והסיומת של שם‬
‫קובץ הפלט תשאר ‪.PGM‬‬

‫הריצו תוכנית זו עם מטריצות המסיכה הבאות‪:‬‬

‫‪k=4‬‬ ‫‪k=3‬‬ ‫‪k=2‬‬

‫הערה‪ :‬במימוש האלגוריתם אין הכרח לעבור מעשית דרך כל השלבים‪ .‬מימוש חכם יתייחס אליהם כאל‬
‫שלבים לוגיים בלבד‪) .‬לא באמת צריך ליצור את המטריצה ‪ ,B‬ואין צורך לשכפל מעשית מטריצה( ניתן‬
‫לממש את האלגוריתם במעבר אחד על התמונה ובעזרת פחות מ‪ 15-‬שורות קוד‪ ,‬חישבו איך(‪.‬‬

‫ג‪.‬‬
‫חזרו על הסעיפים א' ו‪-‬ב' אך הפעם במקום קובץ ‪ PPM‬עם ‪ P3 :Magic Number‬השתמשו בקובץ‬
‫‪ PPM‬עם ‪ P6 :Magic Number‬ובמקום קובץ ‪ PGM‬עם ‪ P2 :Magic Number‬השתמשו בקובץ‬
‫‪ PGM‬עם ‪.P5 :Magic Number‬‬

‫הנחיות לכל הסעיפים‪:‬‬


‫על התוכניות השונות להתמודד עם כל קובץ ‪ PPM ,PGM‬חוקי‪.‬‬ ‫•‬
‫בכדי לייצר קבצי קלט מטיפוס ‪ PPM‬לבדיקת התוכנית‪ ,‬ניתן להשתמש בתוכנות הממירות תמונות‬ ‫•‬
‫מפורמטים נפוצים )לדוגמא ‪ (jpeg, bmp, gif‬לפורמטים ‪.PGM, PPM‬‬
‫האתר‪ /[Link] :‬מספק שירות המרה בין פורמטים שונים בחינם‪ ,‬אולם‬
‫הוא ממיר לפורמטים ‪ P6‬ו‪ P5-‬בלבד‪ .‬מכאן תוכלו לייצר בעצמכם את הפורמטים החסרים‪.‬‬

‫‪- 21 -‬‬
PPM File Format
This note describes the format of PPM (Portable PixMap) file. This format is a
convenient (simple) method of saving a color image data.
A PPM file consists of two parts, the header and the image data. The header consists
of three parts that are delimitated by white space (usually linefeeds - '\n').

Header format:
The first part is a magic PPM identifier; it can be the string "P3" or the string "P6"
(not including the double quotes!).
The next part consists of the width and height of the image as ASCII numbers.
The last part of the header gives the maximum value of the color components (depth)
for the pixels. This value must be smaller than 256 and greater than 0.
In addition to the above required lines, a comment can be placed anywhere before the
depth starting with a "#" character, the comment extends to the end of the line.
The following are all valid PPM headers.

example 1: Header
P6 1024 788 255

example 2: Header
P6
1024 788
# A comment
255

example 3: Header
P3
1024 # the image width
788 # the image height
# A comment
15

Image data format:


The format of the image data itself depends on the magic PPM identifier. If it is "P3"
then the image is given as ascii text, the numerical value of each pixel ranges from 0
to the depth given in the header.

example 4: PPM P3
P3
# example from the man page
4 4
15
0 0 0 0 0 0 0 0 0 15 0 15
0 0 0 0 15 7 0 0 0 0 0 0
0 0 0 0 0 0 0 15 7 0 0 0
15 0 15 0 0 0 0 0 0 0 0 0

If the PPM magic identifier is "P6" then the image data is stored in binary format, one
byte per color component (r,g,or b).
Comments can not appear in the image data section. Only one byte of a whitespace
may appear after the last header field, normally a '\n'.

- 22 -
PGM File Format
This format is identical to the above except it stores greyscale information, that is, one
value per pixel instead of 3 (r,g,b). The only difference in the header section is the magic
identifiers which are "P2" and "P5", these correspond to the ascii and binary form of the
data respectively.

PGM example
An example of a PGM file of type "P2" is given below
P2
24 7 #dimension
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 0 0 0 7 7 7 0 0 0 11 11 11 0 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 0 0 0 11 0 0 0 0 0 15 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

- 23 -
Summary of the PPM/PGM file formats:
1. A "magic number" for identifying the file type. Consist of two ASCII
characters (one of the following):
"P6" – for PPM (color) Binary image data
"P3" – for PPM (color) ASCII image data
"P5" – for PGM (grayscale) Binary image data
"P2" – for PGM (grayscale) ASCII image data
2. Whitespaces (blanks, TABs, CRs, LFs).
3. A width, formatted as ASCII characters in decimal.
4. Whitespaces.
5. A height, again in ASCII decimal.
6. Whitespaces.
7. The maximum color value (depth), again in ASCII decimal. Must be less than
256 and more than zero.
8. A single whitespace character.
9. In P6 magic number: A block of Height rows, in order from top to bottom.
Each row consists of Width pixels, in order from left to right. Each pixel is a
triplet of red, green, and blue samples, in that order. Each sample is
represented in pure binary using one byte.
In P3 magic number: A block of Height rows, in order from top to bottom.
Each row consists of Width pixels, in order from left to right. Each pixel is a
triplet of red, green, and blue samples, in that order. Each sample is
represented as an ASCII decimal number. Each sample has white space before
and after it. There must be at least one character of white space between any
two samples, but there is no maximum. There is no particular separation of
one pixel from another -- just the required separation between the blue sample
of one pixel from the red sample of the next pixel.
In P5 magic number: A block of Height rows, in order from top to bottom.
Each row consists of Width pixels, in order from left to right. The gray level of
each pixel is represented in pure binary using exactly one byte.
In P2 magic number: A block of Height rows, in order from top to bottom.
Each row consists of Width pixels, in order from left to right. The gray level of
each pixel is represented as an ASCII decimal number. Each gray level has
white spaces before and after it. There must be at least one character of white
space between any two gray levels, but there is no maximum.
10. In general, characters from a "#" to the next end-of-line, before the depth line,
are comments and are ignored.

- 24 -
‫שאלה ‪3‬‬
‫נתונה ההגדרה הבאה של רשומה שמירת נתונים על תלמיד‪:‬‬
‫{‪typedef struct student‬‬
‫;‪char* name‬‬
‫;‪int average‬‬
‫;‪}STUDENT‬‬

‫מאגר של תלמידים נשמר בקובץ בינארי לפי הפורמט הבא‪:‬‬


‫• מספר מטיפוס ‪ short int‬המציין את מספר רשומות התלמידים הנמצאות בקובץ‪ ,‬נסמנו ב‪.n -‬‬
‫• רצף של ‪ n‬רשומות‪ ,‬כאשר כל רשומה בנויה באופן הבא‪:‬‬
‫• מספר מטיפוס ‪ short int‬המייצג את מספר התווים בשמו של התלמיד הנוכחי ‪ ,‬נסמנו‬
‫מ‪.len -‬‬
‫• רצף של ‪ len‬תווים‪ ,‬המייצגים את שמו של התלמיד הנוכחי )אין '‪ '\0‬בסוף רצף התווים‬
‫הנ"ל(‪.‬‬
‫• מספא מטיפוס ‪ int‬המייצג את ממוצע ציוניו של התלמיד הנוכחי‪.‬‬

‫א‪ .‬כתבו תוכנית המקבלת כפרמטר שם של קובץ המכיל נתוני תלמידים עפ"י הפורמט לעיל‪ .‬התוכנית‬
‫תיצור עבור קובץ התלמידים קובץ אינדקסים )קובץ המכיל נתונים של היסט יחסית לתחילת קובץ‬
‫התלמידים(‪ ,‬כך שאם נעבור על ההיסטים ברצף נקבל את התלמידים בסדר לקסיקוגראפי עולה של שמות‪.‬‬
‫על שמו של קבוץ האינדקסים להיות השם של הקובץ המקורי בתוספת הסיומת "‪.".ind‬‬

‫הערה‪ :‬את המיון יש לבצע ע"י אלגוריתם יעיל ככל האפשר‪.‬‬

‫ב‪ .‬כתבו את הפונקציה הבאה‪:‬‬


‫;)‪int findAverageGrade(char* database, char* studName‬‬

‫הפונקציה מקבלת כקלט מחרוזת ‪ ,database‬המייצגת שם של קובץ המכיל נתוני תלמידים )לפי‬
‫הפורמט שהוצג בתחילת השאלה(‪ ,‬ומחרוזת ‪ ,studName‬המכילה שם של תלמיד‪.‬‬
‫על הפונקציה להחזיר את ממוצע הציונים של התלמיד ששמו ‪ studName‬או ‪ -1‬אם אין תלמיד כזה ב‪-‬‬
‫‪.database‬‬

‫הערות‪:‬‬
‫‪ .1‬הניחו שהפונקציה הנ"ל נקראת רק אחרי שכבר נוצר קובץ אינדקסים‪ ,‬כפי שהוגדר בסעיף א'‪ ,‬עבור‬
‫הקובץ ‪.database‬‬
‫‪ .2‬הניחו שבקובץ ישנו לכל היותר תלמיד אחד עם השם ‪.studName‬‬
‫‪ .3‬שימו לב ליעילות זמן הריצה של הפונקציה אותה אתם כותבים‪.‬‬

‫‪- 25 -‬‬
‫ביטים‬
‫שאלה ‪1‬‬
‫חיפוש בספר טלפונים‪.‬‬
‫נניח כי לכל לקוח בספר טלפונים יש ‪ 2‬מחרוזות‪:‬‬
‫‪typedef struct client‬‬
‫{‬
‫;]‪char id[9‬‬ ‫”‪// “12345678‬‬
‫”‪char phone[11]; // “03:1234567‬‬
‫;‪}t_client‬‬
‫בשדה ת‪.‬ז‪ .‬ישנן ‪ 8‬ספרות )ללא ספרת ביקורת( ובסוף ‘‪.’\0‬‬
‫בשדה טלפון יש שתי ספרות קידומת‪ ,‬סימן הפרדה ‘‪) ’:‬נשים לב כי בטבלת ‪ ascii‬הוא מופיע מיד‬
‫לאחר ‘‪ ,(’9‬אח"כ ‪ 7‬ספרות המספר עצמו ו – ‘‪ ’\0‬בסוף‪.‬‬
‫בסה"כ נשמרים ‪ 20‬בתים ללקוח‪.‬‬

‫נרצה להקטין את כמות הבתים הנשמרת ללקוח‪ ,‬מבלי לאבד אינפורמציה‪.‬‬


‫נשתמש במבנה הבא‪:‬‬
‫‪typedef struct short_client‬‬
‫{‬
‫;]‪unsigned char shorted[4‬‬
‫;]‪unsigned char short_phone[5‬‬
‫;‪}t_short_client‬‬

‫כל התווים המשתתפים במידע שנשמר ללקוח הינם עוקבים בטבלת ‪.ascii‬‬
‫מדובר בתווים‪’:‘, ’9’…, ’2’,’1’,’0‘ :‬‬
‫ישנם בסה"כ ‪ 11‬תווים‪.‬‬
‫ניתן לקודד מחדש כל תו ב – ‪ 4‬סיביות באופן הבא‪:‬‬
‫‪0000‬‬ ‫’‪‘0‬‬
‫‪0001‬‬ ‫’‪‘1‬‬
‫‪0010‬‬ ‫’‪‘2‬‬
‫‪0011‬‬ ‫’‪‘3‬‬
‫‪0100‬‬ ‫’‪‘4‬‬
‫‪0101‬‬ ‫’‪‘5‬‬
‫‪0110‬‬ ‫’‪‘6‬‬
‫‪0111‬‬ ‫’‪‘7‬‬
‫‪1000‬‬ ‫’‪‘8‬‬
‫‪1001‬‬ ‫’‪‘9‬‬
‫‪1010‬‬ ‫’‪‘:‬‬
‫קידוד זה מאפשר לשמור את המידע הרצוי במבנה החדש ‪.t_short_client‬‬

‫א‪ .‬כתוב פונקציה המקבלת מספר ‪ n‬ומבצעת את הפעולות הבאות‪:‬‬


‫‪ .1‬הקצאת מערך דינאמי של ‪ n‬רשומות מסוג ‪t_client‬‬
‫‪ .2‬קליטת נתונים לתוך אברי המערך‪.‬‬
‫‪ .3‬דחיסת הנתונים במערך למערך דינאמי של רשומות מסוג ‪ t_short_client‬תוך שחרור‬
‫המערך הישן‪.‬‬
‫‪ .4‬החזרת המערך הדחוס‪.‬‬
‫ב‪ .‬כתוב פונקציה המאפשרת חיפוש בספר טלפונים‬
‫‪ .1‬קלוט מספר ת‪.‬ז‪ .‬כמחרוזת של ‪ 9‬תווים‪.‬‬
‫‪ .2‬העבר אותו לצורה הדחוסה‬
‫‪ .3‬חפש אותו בלולאה במערך הדחוס‪ .‬כאשר החיפוש מצליח‪ ,‬על הפונקציה להחזיר את המחרוזת‬
‫המציינת מהו מספר הטלפון של הלקוח‪) .‬המחרוזת הינה בצורה המקורית ולא הדחוסה‪(.‬‬
‫ג‪ .‬כתוב תוכנית המאפשרת ביצוע הפעולות הנ"ל‬
‫‪- 26 -‬‬
‫שאלה ‪2‬‬

‫א‪ .‬על מנת לצמצם את המקום הנדרש לייצג מחרוזות בת ‪ n‬תווים‪ ,‬שבה מספר התווים השונים הוא לכל‬
‫היותר ‪ .16‬במקום שנידרש ל‪ n -‬בתים כדי לשמור אותה עפ"י קוד ‪ ,ascii‬נצטמצם ל‪: n2 -‬‬

‫לכל תו נצמיד מספר בין ‪ 0‬ל‪ 4) 15 -‬ביטים(‪ .‬כך שבכל ‪ byte‬יהיו שני תווים‪.‬‬
‫תצטמצם להיות‪:‬‬ ‫למשל המחרוזת‪:‬‬
‫‪a‬‬ ‫‪c‬‬ ‫'‪'a‬‬
‫‪b‬‬ ‫‪a‬‬ ‫'‪'c‬‬
‫'‪'b‬‬
‫'‪'a‬‬
‫'‪'\0‬‬

‫אם נסתכל בסדרת התווים השונים מהמחרוזת המקורית‪ ,‬מסודרים לפי סדר הופעתם‬
‫הראשונה‪ ,‬הערך שיוצמד לכל תו יהיה מיקומו בסדרה זו )החל מ‪.(0 -‬‬
‫למשל‪ :‬אם המחרוזת המקורית היא‪:"acba" :‬‬
‫ל‪ ,a -‬יוצמד הערך ‪.0‬‬
‫ל‪ ,c -‬יוצמד הערך ‪.1‬‬
‫ל‪ ,b -‬יוצמד הערך ‪.2‬‬
‫‪0000 0001‬‬
‫‪0010 0000‬‬ ‫ולכן המחרוזת המצומצמת תהיה‪:‬‬

‫ממשו ראשית את הפונקציה‪:‬‬


‫;)‪char* getUniqueLetters(char* text, int* size‬‬
‫המקבלת מחרוזת ‪ ,text‬ומחזירה מערך המכיל את התווים השונים המופיעים ב‪ ,text -‬מסודרים‬
‫במערך לפי סדר הופעתם הראשונה ב‪ .text -‬הפונקציה גם מעדכנת בכתובת שנשלחה לה במשתנה‬
‫‪ ,size‬את גודלו של המערך המוחזר‪.‬‬
‫למשל‪ :‬אם תופעל הפונקציה עם "‪ ,text="mullyllum‬היא תחזיר את המערך‪:‬‬
‫]'‪ ,['m', 'u', 'l', 'y‬ותעדכן בכתובת של ‪ size‬את הערך ‪.4‬‬
‫שימו לב‪ :‬הערך המוחזר של הפונקציה הוא מערך של תווים‪ ,‬ולא מחרוזת )לא מסתיים ב‪.('\0' -‬‬

‫כתבו תוכנית המקבלת כפרמטר שם של קובץ טקסט‪ .‬ידוע שהקובץ הנ"ל מורכב משורות באורך זוגי של‬
‫תווים‪ ,‬שבכל שורה יש לכל היותר ‪ 16‬תווים שונים‪.‬‬

‫על התוכנית להמיר קובץ זה לקובץ בינארי הבנוי לפי הפורמט הבא‪:‬‬
‫עבור כל שורה בקובץ הטקסט‪ ,‬ישמרו בקובץ הבינארי המיוצר‪ ,‬הנתונים הבאים‪:‬‬
‫‪ .1‬מספר מטיפוס ‪ – unsigned char‬המייצג את מספר התווים השונים בשורה‪.‬‬
‫‪ .2‬רצף )באורך זה( של ‪-char‬ים – המכיל את סדרת התווים השונים בשורה‪ ,‬מסודרים‬
‫לפי סדר הופעתם הראשונה באותה שורה‪.‬‬
‫‪ .3‬מספר מטיפוס ‪ – unsigned int‬המייצג את מספר הבתים בהם נשמרים התווים‬
‫המכווצים של השורה‪.‬‬
‫‪ .4‬רצף )באורך זה( של בתים המכילים את הכווץ של השורה‪.‬‬

‫שמו של הקובץ המיוצר הוא כשמו של קובץ הקלט‪ ,‬בתוספת "‪ ".rds‬בסוף‪.‬‬

‫‪- 27 -‬‬
‫למשל‪ :‬נניח ש‪ "[Link]" -‬הוא קובץ טקסט‪ ,‬המכיל את שתי השורות‪:‬‬

‫‪acba‬‬
‫‪mbbmbmmb‬‬

‫אם תועבר לתוכנית‪ ,‬כפרמטר בשורת ההפעלה‪ ,‬המחרוזת "‪ ,"[Link]‬יווצר הקובץ‬
‫"‪ ."[Link]‬קובץ זה יכיל‪:‬‬
‫‪3‬‬
‫'‪'a‬‬
‫'‪'c‬‬
‫שורה ‪I‬‬
‫'‪'b‬‬
‫‪2‬‬
‫‪0000‬‬ ‫‪0001‬‬
‫‪0010‬‬ ‫‪0000‬‬
‫‪2‬‬
‫'‪'m‬‬
‫'‪'b‬‬
‫שורה ‪II‬‬ ‫‪4‬‬
‫‪0000‬‬ ‫‪0001‬‬
‫‪0001‬‬ ‫‪0000‬‬
‫‪0001‬‬ ‫‪0000‬‬
‫‪0000‬‬ ‫‪0001‬‬

‫‪- 28 -‬‬
‫ב‪ .‬כעת נרצה לשחזר מחרוזת מכווצת המכילה לכל היותר ‪ 16‬תווים שונים‪ ,‬למחרוזת רגילה‪.‬‬
‫כל בית במחרוזת המכווצת מכיל שני תווים מהמחרוזת הרגילה‪ .‬כל תו תופס ‪ 4‬ביטים‪ .‬ארבעת הביטים‬
‫השמאליים מייצגים את התו הראשון )שנשמר בבית הזה(‪ ,‬וארבעת הביטים הימניים מייצגים את התו‬
‫השני )שנשמר בבית הזה(‪.‬‬
‫את השחזור נבצע בעזרת מערך ‪ code‬המכיל את כל התווים השונים של המחרוזת‪ ,‬כל תו בדיוק פעם‬
‫אחת‪.‬‬
‫במחרוזת המכווצת במקום כל תו שומרים ‪ 4‬ביטים המייצגים את המיקום של אותו תו במערך ‪.code‬‬

‫)מכילה ‪ 4‬תווים(‪,‬‬ ‫‪0000‬‬ ‫‪0001‬‬ ‫למשל אם המחרוזת המכווצת היא‪:‬‬


‫והמערך ]'‪,code = ['a', 'c', 'b‬‬
‫‪0010‬‬ ‫‪0010‬‬
‫אזי המחרוזת המשוחזרת תהיה‪."acbb" :‬‬

‫כתבו תוכנית המקבלת כפרמטר שם של קובץ בינארי השומר בצורה מכווצת‪ ,‬בפורמט שיוגדר בהמשך‪,‬‬
‫שורות באורך זוגי של תווים‪ ,‬שבכל שורה לכל היותר ‪ 16‬תווים שונים‪.‬‬
‫על התוכנית להמיר קובץ זה לקובץ טקסט‪ .‬שמו של הקובץ המיוצר הוא כשמו של קובץ הקלט‪,‬‬
‫בתוספת "‪ ".xpd‬בסוף‪.‬‬

‫פרמט הקובץ הבינארי )שהתקבל כקלט(‪:‬‬


‫עבור כל שורה מכווצת נשמרים הנתונים הבאים‪:‬‬
‫‪ .1‬מספר מטיפוס ‪ – unsigned char‬המייצג את מספר התווים השונים בשורה‪.‬‬
‫‪ .2‬רצף )באורך זה( של ‪-char‬ים – המכיל את סדרת התווים השונים בשורה‪.‬‬
‫‪ .3‬מספר מטיפוס ‪ – unsigned int‬המייצג את מספר הבתים בהם נשמרים התווים‬
‫המכווצים של השורה‪.‬‬
‫‪ .4‬רצף )באורך זה( של בתים המכילים את הכווץ של השורה‪.‬‬
‫שימו לב‪ :‬בקובץ הבינארי נשמרים נתונים המייצגים מחרוזת מכווצת‪ ,‬כמו שתואר בסעיף הקודם‪.‬‬

‫‪3‬‬ ‫לדוגמא‪ :‬אם "‪ "[Link]‬הוא הקובץ הבינארי הבא‪:‬‬


‫'‪'a‬‬
‫'‪'c‬‬
‫שורה ‪I‬‬
‫'‪'b‬‬
‫‪2‬‬
‫‪0000‬‬ ‫‪0001‬‬
‫‪0010‬‬ ‫‪0000‬‬
‫‪2‬‬
‫'‪'m‬‬
‫'‪'b‬‬
‫שורה ‪II‬‬ ‫‪4‬‬
‫‪0000‬‬ ‫‪0001‬‬
‫‪0001‬‬ ‫‪0000‬‬
‫‪0001‬‬ ‫‪0000‬‬
‫‪0000‬‬ ‫‪0001‬‬

‫אז התוכנית תיצור את הקובץ "‪ ,"[Link]‬אשר יכיל את שתי השורות‪:‬‬


‫‪acba‬‬
‫‪mbbmbmmb‬‬
‫‪- 29 -‬‬
‫שאלה ‪3‬‬
‫א‪ .‬נתון תו מוצפן‪ .ch ,‬על מנת לפענח אותו יש לשנות את מיקום הביטים בו‪ .‬השינוי יעשה בעזרת‬
‫תמורה ]‪ [a0, a1, …, a7‬של המספרים ‪ .0-7‬תהליך הפענוח יעשה באופן הבא‪:‬‬
‫• את הביט האפס של התו ‪ ch‬נשים בביט שנמצא במקום ה‪ a0 -‬בתו המפוענח‪.‬‬
‫• את הביט האחד של התו ‪ ch‬נשים בביט שנמצא במקום ה‪ a1 -‬בתו המפוענח‪.‬‬
‫• וכך הלאה‪...‬‬
‫הערה חשובה‪ :‬הביט ה‪ 0 -‬הוא הביט הימני ביותר‪.‬‬
‫למשל אם נרצה לפענח את התו שמיוצג בינארית‪ 00110001 :‬בעזרת התמורה‪[3,2,1,0,7,6,5,4] :‬‬
‫נקבל את התו המפוענח שמיוצג בינארית‪) 11001000 :‬למשל הביט האפס של התו המקורי )‪ (1‬עבר‬
‫לביט ה‪ 3 -‬בתו המפוענח‪ ,‬וכו'(‪.‬‬

‫;)‪char decode(char ch, int* key‬‬ ‫כתבו את הפונקציה‪:‬‬


‫הפונקציה מקבלת תו מוצפן ‪ ,ch‬ומערך ‪ key‬בגודל ‪ 8‬המכיל תמורה כלשהי של המספרים ‪.0-7‬‬
‫הפונקציה תפענח את התו ‪ ,ch‬עפ"י השיטה שתוארה לעיל‪ ,‬ותחזיר את התו המפוענח‪.‬‬

‫ב‪ .‬השתמש בפונקציה מסעיף א' על מנת לכתוב את הפונקציה‪:‬‬


‫;)‪char* decodeString(char* name, int* key‬‬
‫הפונקציה מקבלת מחרוזת ‪ name‬המכילה שם מקודד‪ ,‬ומערך ‪ key‬בגודל ‪ 8‬המכיל תמורה כלשהי של‬
‫המספרים ‪ .0-7‬הפונקציה תיצור ותחזיר מחרוזת חדשה שתכיל את השם המפוענח‪ .‬פענוח השם יתבצע‬
‫ע"י פענוח כל אחד מתווי ‪ name‬עפ"י התמורה ‪.key‬‬

‫ג‪ .‬שירות הביטחון הכללי שומר שני קבצים עבור כל פעולה שהוא מבצע‪.‬‬
‫‪ .1‬קובץ בינארי )שאותו ניתן אפילו לשלוח ב‪ (mail -‬המחזיק בצורה מוצפנת שמות של הסוכנים‬
‫המשתתפים במבצע‪.‬‬
‫‪ .2‬קובץ טקסט )שנשמר בכספת( המכיל את התמורות שבעזרתן ניתן לפענח את השמות המקודדים‬
‫בקובץ הבינארי‪.‬‬

‫פורמט קובץ השמות המקודדים )הקובץ הבינארי(‪:‬‬


‫שמות הסוכנים נשמרים ברצף‪ .‬כל שם )מקודד( נשמר באופן הבא‪:‬‬
‫מספר שלם )‪ (short int‬המייצג את אורך שמו של הסוכן ואחריו רצף באורך זה של תווים מוצפנים‬
‫המרכיבים יחד את שמו‪.‬‬
‫פורמט קובץ המפתחות )קובץ הטקסט(‪:‬‬
‫הקובץ מורכב משורות‪ .‬בכל שורה תופיע תמורה של המספרים ‪ 0-7‬המופרדים ברווחים‪.‬‬
‫סדר השורות בקובץ מתאים לסדר בו נשמרו השמות המקודדים בקובץ השמות‪ .‬בפרט מספר השורות הוא‬
‫כמספר הסוכנים בקובץ השמות‪.‬‬

‫בהינתן ההגדרה הבאה למבנה עבור רשימה מקושרת‪:‬‬


‫{‪typedef struct list‬‬
‫;‪LNODE* head‬‬
‫;‪LNODE* tail‬‬
‫;‪}LIST‬‬
‫{‪typedef struct l_node‬‬
‫;‪char* name‬‬
‫;‪struct l_node* next‬‬
‫;‪}L_NODE‬‬
‫כתבו את הפונקציה‪:‬‬
‫;)‪LIST decodeMission(FILE* agents, FILE* keys‬‬
‫הפונקציה מקבלת מצביעים לשני קבצים‪:‬‬
‫• ‪ - agents‬קובץ בינארי המכיל שמות מקודדים של סוכנים בפורמט שתואר לעיל‪.‬‬
‫• ‪ - keys‬קובץ טקסט המכיל מפתחות בפורמט שתואר לעיל‪.‬‬
‫הפונקציה תחזיר רשימה‪ ,‬שתכיל את שמותיהם המפוענחים של הסוכנים המופיעים בקובץ ‪ .agents‬על‬
‫הרשימה להכיל את הסוכנים לפי סדר הופעתם בקובץ‪.‬‬
‫‪- 30 -‬‬
‫שאלה ‪4‬‬
‫א‪ .‬כתבו את הפונקציה‪:‬‬
‫‪int* filter(int* Numbers, int size, unsigned char* pred,‬‬
‫;)‪int* new_size‬‬

‫הפונקציה מקבלת מערך ‪ Numbers‬של מספרים שלמים שונים זה מזה‪ ,‬ואת גודלו ‪ .size‬כמו כן היא‬
‫‪ ⎡⎢ size‬בתים‪ .‬כל ביט במערך ‪ ,pred‬מייצג נתון אחד של‬‫⎤ ‪8‬‬
‫מקבלת מערך ‪ pred‬של ביטים‪ ,‬בגודל ⎥‬
‫המערך ‪ ,Numbers‬באופן הבא‪:‬‬
‫הביט ה‪ 0 -‬בבית ה‪ 0 -‬של המערך ‪ ,pred‬מייצג את הנתון באינדקס ‪ 0‬במערך ‪.Numbers‬‬
‫הביט ה‪ 1 -‬בבית ה‪ 0 -‬של המערך ‪ ,pred‬מייצג את הנתון באינדקס ‪ 1‬במערך ‪.Numbers‬‬

‫הביט ה‪ 0 -‬בבית ה‪ 1 -‬של המערך ‪ ,pred‬מייצג את הנתון באינדקס ‪ 8‬במערך ‪.Numbers‬‬


‫וכך הלאה‪.‬‬

‫הפונקציה תייצר ותחזיר את תת המערך המכיל אך ורק את הנתונים מהמערך ‪ ,Numbers‬אשר הביט‬
‫המתאים להם במערך ‪ ,pred‬הוא ‪.1‬‬
‫כמו כן‪ ,‬הפונקציה תעדכן במשתנה הפלט ‪ new_size‬את גודלו של תת המערך שייצרה‪.‬‬

‫‪Numbers‬‬ ‫למשל‪ ,‬אם המערכים ‪ Numbers‬ו‪ ,pred -‬מוגדרים באופן הבא‪:‬‬

‫‪8‬‬ ‫‪4‬‬ ‫‪2‬‬ ‫‪6‬‬ ‫‪30‬‬ ‫‪22‬‬ ‫‪28‬‬ ‫‪32‬‬ ‫‪20‬‬ ‫‪26‬‬ ‫‪24‬‬ ‫‪10‬‬ ‫‪16‬‬ ‫‪14‬‬ ‫‪18‬‬ ‫‪12‬‬

‫‪pred‬‬
‫‪00101001‬‬
‫‪01001000‬‬

‫הקריאה )‪ ,filter(Numbers, pred, 16, size‬תייצר את המערך‪:‬‬


‫וכן תעדכן במשתנה אליו מצביע ‪ new_size‬את הערך ‪.5‬‬
‫‪8‬‬ ‫‪6‬‬ ‫‪22‬‬ ‫‪10‬‬ ‫‪18‬‬

‫הערות‪:‬‬
‫• הניחו ש‪ ,size -‬מספר הנתונים במערך ‪ ,Numbers‬מתחלק ב‪.8 -‬‬
‫• שימו לב כי בציור‪ ,‬האינדקסים במערך המספרים מתקדמים משמאל לימין‪ ,‬ואילו הביטים בכל בית‬
‫נספרים מימין לשמאל )מה‪ LSB -‬אל ה‪.(MSB -‬‬

‫ב‪ .‬השתמשו בפונקציה מסעיף א'‪ ,‬על מנת לכתוב הפונקציה הבאה‪:‬‬
‫‪int* xorFilter(int* Numbers, int size, unsigned char* pred1,‬‬
‫;)‪unsigned char* pred2, int* new_size‬‬

‫הפונקציה מקבלת מערך ‪ Numbers‬של מספרים שלמים שונים זה מזה‪ ,‬ואת גודלו ‪ .size‬כמו כן היא‬
‫‪ ⎡⎢ size‬בתים‪ .‬כל אחד‬
‫⎤ ‪8‬‬ ‫מקבלת שני מערכים ‪ pred1‬ו‪ pred2 -‬של ביטים‪ ,‬כל אחד מהם בגודל ⎥‬
‫מהביטים‪ ,‬בכל אחד מהמערכים ‪ pred1‬ו‪ ,pred2 -‬מייצג נתון מסוים של המערך ‪ ,Numbers‬לפי‬
‫ההתאמה שתוארה בסעיף א'‪.‬‬
‫הפונקציה תייצר ותחזיר את תת המערך המכיל אך ורק את נתונים מהמערך ‪ .Numbers‬נתון מסוים‬
‫יופיע במערך התוצאה אם"ם בדיוק אחד מביו הביטים המתאימים לו במערך ‪ ,pred1‬ו‪ ,pred2 -‬הוא‬
‫‪ .1‬כמו כן‪ ,‬הפונקציה תעדכן במשתנה הפלט ‪ new_size‬את גודלו של תת המערך שייצרה‪.‬‬
‫הערה‪ :‬הניחו ש‪ ,size -‬מספר הנתונים במערך ‪ ,Numbers‬מתחלק ב‪.8 -‬‬
‫‪- 31 -‬‬
‫פויינטרים לפונקציות‬

‫שאלה ‪1‬‬
‫כתבו את הפונקציה‪:‬‬
‫;)‪void* scramble(void* arr, int ElemSize, int n, int* indArr‬‬

‫הפונקציה מקבלת‪:‬‬
‫• ‪ - arr‬מערך של איברים מטיפוס כלשהו‪.‬‬
‫• ‪ - ElemSize‬גודל של כל אלמנט במערך ‪arr‬‬
‫• ‪ - n‬מספר האיברים במערך ‪.arr‬‬
‫• ‪ - indArr‬מערך המכיל תמורה של המספרים‪. 0,1,… , (n − 1) :‬‬

‫על הפונקציה ליצור מערך חדש שבו סדר האיברים נקבע על פי המערך ‪ indArr‬באופן הבא‪:‬‬
‫האיבר ה ‪ i -‬במערך החדש יהיה האיבר שהאינדקס שלו במערך הישן הוא ]‪.indArr[i‬‬

‫למשל‪ ,‬אם ‪ n = 3‬ו‪ indArr = [2, 0,1] -‬אז‪:‬‬


‫במקום ה‪ 0 -‬של המערך החדש ימצא האיבר מהמקום ה‪ 2 -‬של ‪.arr‬‬
‫במקום ה‪ 1 -‬של המערך החדש ימצא האיבר מהמקום ה‪ 0 -‬של ‪.arr‬‬
‫במקום ה‪ 2 -‬של המערך החדש ימצא האיבר מהמקום ה‪ 1 -‬של ‪.arr‬‬

‫שאלה ‪2‬‬
‫נתון האלגוריתם הבא למימוש חיפוש בינארי של נתון במערך‪:‬‬

‫מערך של נתונים – ‪.Arr‬‬ ‫קלט‪:‬‬


‫גודל המערך ‪Size -‬‬
‫נתון לחיפוש – ‪.Item‬‬
‫)‪ (true‬אם"ם )‪ Item‬מופיע במערך ‪.(Arr‬‬ ‫פלט‪:‬‬

‫‪found Åfalse‬‬ ‫‪.1‬‬


‫‪left Å 0‬‬ ‫‪.2‬‬
‫)‪right Å (Size-1‬‬ ‫‪.3‬‬
‫כל עוד ))‪( ! found ∧ (left ≤ right‬‬ ‫‪.4‬‬
‫‪place Å( (left+right) div 2) 4.1‬‬
‫‪ 4.2‬אם )‪(Arr[place]=Item‬‬
‫‪found Å true 4.2.1‬‬
‫‪ 4.3‬אחרת אם )‪(Arr[place]<Item‬‬
‫‪left Å (place+1) 4.3.1‬‬
‫‪ 4.4‬אחרת‬
‫‪rightÅ (place-1) 4.4.1‬‬
‫החזר את ‪found‬‬ ‫‪.5‬‬

‫‪- 32 -‬‬
‫א‪ .‬כתבו את הפונקציה הבאה‪:‬‬
‫‪int binSearch(void* Arr, int Size, int ElemSize,‬‬
‫;))*‪void* Item, int (*compare)(void*, void‬‬
‫על הפונקציה לממש אלגוריתם זה ‪ ,‬למערכים מטיפוסים שונים )מערכים של מחרוזות‪ ,‬מערכים של‬
‫‪-int‬ים וכו'(‪ .‬הפונקציה מקבלת‪:‬‬

‫‪ - Arr‬כתובת התחלה של מערך‪.‬‬ ‫•‬


‫‪ - Size‬מספר הנתונים בו‪.‬‬ ‫•‬
‫‪ - ElemSize‬מספר הבתים שתופס כל אחד מהנתונים‪.‬‬ ‫•‬
‫‪ - Item‬כתובת של נתון שלגביו יש להכריע האם הוא נמצא במערך‪ .‬טיפוס הנתון הוא‬ ‫•‬
‫כטיפוס נתוני המערך‪.‬‬
‫‪ - compare‬פויינטר לפונקציה המשווה בין שני נתונים )מהטיפוס של הנתונים מהמערך(‪.‬‬ ‫•‬

‫על הפונקציה להחזיר את הערך ‪ 1‬אם הנתון עליו מצביע ‪ Item‬שווה לאחד מנתוני המערך‪ ,‬או ‪ 0‬אחרת‪.‬‬

‫הערה‪:‬‬
‫הניחו שהפונקציה ‪ ,compare‬מקבלת כתובות של שני נתונים אותם היא משווה‪ .‬ומחזירה‪:‬‬
‫• אפס ‪ -‬אם שני הפרמטרים מצביעים על נתונים שווים‪.‬‬
‫• מספר שלילי ‪ -‬אם הפרמטר הראשון מצביע על נתון קטן יותר מהנתון שמוצבע ע"י הפרמטר‬
‫השני‪.‬‬
‫• מספר חיובי ‪ -‬אם הפרמטר הראשון מצביע על נתון גדול יותר מהנתון שמוצבע ע"י הפרמטר‬
‫השני‪.‬‬

‫ב‪ .‬השתמשו בפונקציה ‪,BinSearch‬שכתבתם‪ ,‬על מנת לכתוב את הפונקציה‪:‬‬


‫;)‪int stringBinSearch(char** strings, int size, char* str‬‬

‫המקבלת מערך ‪ strings‬של מחרוזות ממוינות בסדר לקסיקוגרפי‪ ,‬את גודלו ‪ ,size‬ומחרוזת‬
‫נוספת ‪.str‬‬
‫על הפונקציה להחזיר את הערך ‪ 1‬אם המחרוזת ‪ str‬מופיעה במערך ‪ ,strings‬או ‪ 0‬אחרת‪.‬‬

‫למשל אם ‪ strings‬הוא המערך‪:‬‬

‫”‪“abcdefg‬‬
‫"‪"good-luck‬‬
‫"‪"mully‬‬
‫"‪"stam‬‬

‫אז ההפעלה‪ stringBinSearch(strings,4,"good-luck") :‬תחזיר ‪.1‬‬

‫‪- 33 -‬‬
‫שאלה ‪3‬‬
‫מאגר של נתונים על מוצרים נשמר בקובץ בינארי‪ .‬הקובץ בנוי בפורמט הבא‪:‬‬
‫‪ .1‬מספר מטיפוס ‪ ,int‬המייצג את מספר המוצרים בקובץ‪ .‬נסמן את ערכו ב‪.N -‬‬
‫‪ .2‬רצף באורך ‪ N‬של נתונים של מוצרים‪ .‬כל מוצר מופיע בקובץ בפורמט הבא‪:‬‬
‫• מספר מטיפוס ‪ ,int‬המייצג את מספר התווים בשמו של המוצר‪ .‬נסמן את ערכו ב‪.len -‬‬
‫• רצף של ‪ len‬תווים‪ ,‬המכיל את שמו של המוצר )ללא '‪ '\0‬בסופו(‪.‬‬
‫• מספר מטיפוס ‪ ,int‬המייצג את מחירו של המוצר‪.‬‬

‫נרצה לייצר שני קבצי אינדקס לקובץ המאגר הנ"ל‪:‬‬


‫• מכיל את ההיסטים )‪ (offsets‬למוצרים בקובץ המאגר‪ ,‬ממוינים לפי שם המוצר )מהקטן לגדול(‪.‬‬
‫• מכיל את ההיסטים למוצרים בקובץ המאגר‪ ,‬ממוינים לפי המחירים שלהם )מהקטן לגדול(‪.‬‬

‫על מנת למנוע שכפול קוד ביצירת שני קבצי האינדקס‪ ,‬נגדיר פונקציה כללית ליצירת קובץ אינדקס‬
‫ממוין‪ ,‬ונפעיל אותה פעמיים ליצירת כל אחד מקבצי האינדקס‪.‬‬
‫בסעיף א' של השאלה נגדיר את הפונקציה הכללית ליצירת קובץ אינדקס ממוין‪ ,‬ובסעיף ב' נייצר את שני‬
‫קבצי האינדקס הרצויים לנו‪.‬‬

‫א‪ .‬כתבו את הפונקציה‪:‬‬


‫‪void createSortedIndex(char* DB_Name, char* IndexFileName,‬‬
‫;))*‪int (*compare) (void*, void‬‬

‫הפונקציה מקבלת מחרוזת ‪ DB_Name‬המכילה את שמו של קובץ בינארי‪ .‬הקובץ מכיל מאגר של נתוני‬
‫מוצרים )לא ממוינים( בפורמט שהוגדר לעיל‪.‬‬
‫הפונקציה מייצרת קובץ אינדקסים‪ ,‬בשם ‪ ,IndexFileName‬המכיל רצף של נתונים מטיפוס‬
‫‪ ,long int‬כל נתון בקובץ האינדקס‪ ,‬מייצג היסט מתחילת קובץ המאגר )‪ (DB_Name‬לתחילת נתוניו‬
‫של מוצר מסוים‪.‬‬
‫אם נעבור על קובץ המאגר‪ ,‬לפי רצף ההיסטים בקובץ האינדקסים‪ ,‬המיוצר ע"י הפונקציה‪ ,‬נקבל את נתוני‬
‫המאגר ממוינים מהקטן לגדול‪ ,‬עפ"י הסדר המושרה מפונקצית ההשוואה ‪.compare‬‬

‫הערה‪ :‬הפונקציה ‪ compare‬הינה פונקצית השוואה כללית‪ ,‬המקבלת מצביעים לשני נתונים ומשווה‬
‫ביניהם‪ .‬הפונקציה משרה סדר באופן הבא‪:‬‬

‫אם הנתון עליו מצביע הפרמטר הראשון קטן מזה שעליו מצביע הפרמטר השני‪ ,‬היא תחזיר מספר‬ ‫•‬
‫שלילי‪.‬‬
‫אם הנתון עליו מצביע הפרמטר הראשון גדול מזה שעליו מצביע הפרמטר השני‪ ,‬היא תחזיר מספר‬ ‫•‬
‫חיובי‪.‬‬
‫אם הנתון עליו מצביע הפרמטר הראשון שווה לזה שעליו מצביע הפרמטר השני‪ ,‬היא תחזיר את‬ ‫•‬
‫הערך ‪.0‬‬

‫לצורך כתיבת הפונקציה ‪ ,createSortedIndex‬ממשו )בין היתר( את הפונקציה‪:‬‬


‫‪void sort(void* Arr, int elem_size, int num_elems,‬‬
‫;))*‪int (*compare) (void*, void‬‬

‫הפונקציה ‪ sort‬מקבלת מצביע ‪ Arr‬לתחילת מערך‪ ,‬מספר שלם ‪ elem_size‬המייצג את גודלו של כל‬
‫נתון במערך‪ ,‬ומספר שלם נוסף ‪ num_elems‬המייצג את מספר הנתונים במערך‪.‬‬
‫הפונקציה ממיינת את הנתונים במערך ‪ ,Arr‬עפ"י הסדר המושרה מפונקצית ההשוואה הכללית‬
‫‪.compare‬‬

‫‪- 34 -‬‬
‫ב‪ .‬השתמשו בפונקציה שכתבתם בסעיף א'‪ ,‬על מנת לכתוב את שתי הפונקציות הבאות‪:‬‬

‫;)‪void indexSortedByName(char* DB_Name, char* IndexFileName‬‬


‫;)‪void indexSortedByPrice(char* DB_Name, char* IndexFileName‬‬

‫כל אחת מהפונקציות מקבלת שם של קובץ ‪ DB_Name‬המכיל מאגר של נתונים על מוצרים‪ ,‬בפורמט‬
‫שפורט בתחילת השאלה‪ .‬כל אחת מהפונקציות מייצרת קובץ אינדקסים בשם ‪) indexFileName‬לפי‬
‫הפורמט שהוגדר בסעיף א'(‪.‬‬
‫אם נעבור על קובץ המאגר‪ ,‬לפי רצף ההיסטים בקובץ אשר מייצרת הפונקציה הראשונה‪ ,‬נקבל את נתוני‬
‫המאגר ממוינים לפי שם המוצרים‪ ,‬מהקטן לגדול‪.‬‬
‫אם נעבור על קובץ המאגר‪ ,‬לפי רצף ההיסטים בקובץ אשר מייצרת הפונקציה השנייה‪ ,‬נקבל את נתוני‬
‫המאגר ממוינים לפי מחיר המוצרים‪ ,‬מהקטן לגדול‪.‬‬

‫‪- 35 -‬‬

You might also like