מתכונת 3 במדעי המחשב - יסודות מדעי המחשב 899371
1–2 יח"ל · שאלון 899371 · 6 שאלות · 2:30 שעות · פתרון מלא לכל סעיף
מבחן מתכונת במדעי המחשב, יסודות מדעי המחשב, במבנה של בחינת הבגרות. כל שאלה בו בנויה כמו שאלה מבגרות אחרת. לכל סעיף יש תשובה ופתרון מלא, וכל קוד מופיע ב-Java וב-C#. פתרו בזמן של הבחינה, ורק אחר כך פתחו את הפתרונות.
הוראות הבחינה
א. משך הבחינה: שעתיים וחצי.
ב. מבנה השאלון ומפתח ההערכה: בשאלון זה שני פרקים.
פרק ראשון – (20×2) – 40 נקודות
פרק שני – (30×2) – 60 נקודות
סך הכול – 100 נקודות
ג. חומר עזר מותר בשימוש: כל חומר עזר, חוץ ממחשבון שיש בו אפשרות תכנוּת.
ד. הוראה מיוחדת:
את כל התוכניות שיש לכתוב בשפת מחשב כתבו בשפה אחת בלבד – Java או C#.
הערה: לא יוּרדוּ נקודות אם תכתבו בתוכניות אות גדולה במקום אות קטנה או להפך.
יש לכתוב במחברת הבחינה בלבד. יש לרשום "טיוטה" בראש כל עמוד המשמש טיוטה.
כתיבת טיוטה בדפים שאינם במחברת הבחינה עלולה לגרום לפסילת הבחינה.
השאלות בשאלון זה מנוסחות בלשון רבים, אף על פי כן על כל תלמידה וכל תלמיד להשיב עליהן באופן אישי.
בהצלחה!
השאלות
בשאלון זה שני פרקים.
יש לענות על שאלות משני הפרקים, לפי ההוראות בכל פרק.
הערה: בכל שאלה שנדרשת בה קליטה, אין צורך לבדוק את תקינות הקלט.
לפותרים בשפת Java: בכל שאלה שנדרשת בה קליטה, הניחו שבתוכנית כתובה ההוראה:
Scanner scan = new Scanner (System.in);
פרק ראשון (40 נקודות)
ענו על שתיים מן השאלות 1–3 (לכל שאלה – 20 נקודות).
שאלה 1 · מעקב – מציינים של שיאים חדשים במערך (20 נקודות)
נתונים שני מערכים מטיפוס שלם: a בגודל 8, ו־ b בגודל 7.
המערך a מכיל מספרים גדולים מ־ 0, והמערך b מכיל כולו אפסים.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| a | 4 | 7 | 7 | 2 | 9 | 5 | 12 | 12 |
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| b | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
נתון קטע התכנית שלפניך, הכתוב ב־ Java וב־ C#.
Java
int max = a[0];
int k = 0;
for(int i=1; i<a.length; i++)
{
if(a[i] > max)
{
max = a[i];
b[k] = i;
k++;
}
}C#
int max = a[0];
int k = 0;
for(int i=1; i<a.Length; i++)
{
if(a[i] > max)
{
max = a[i];
b[k] = i;
k++;
}
}עקבו בעזרת טבלת מעקב אחר ביצוע קטע התכנית בעבור המערכים a ו־ b הנתונים, ורישמו את המערך b לאחר ביצוע קטע התכנית.
בטבלת המעקב יש לכלול עמודה לכל אחד מהמשתנים האלה: b[k], k, max, a[i], i
תנו דוגמה מייצגת למערך a בגודל 8 המכיל מספרים גדולים מ־ 0, שבעבורו, לאחר ביצוע קטע התכנית, בתא b[0] יהיה הערך 7, וכל שאר התאים במערך b יכילו 0.
◂ פתרון מלא לשאלה 1
סעיף 1
b = {1, 4, 6, 0, 0, 0, 0} (k = 3, max = 12)
מה נדרש: לעקוב בטבלת מעקב אחר ביצוע הקטע על המערכים הנתונים, ולרשום את המערך b בסוף הביצוע.
רעיון: max שומר את הערך הגדול ביותר שנראה עד עכשיו (מתחיל ב־a[0]). הלולאה עוברת על a מתא 1 עד הסוף. בכל פעם שמופיע ערך גדול ממש מ־max ("שיא חדש"), max מתעדכן, והמציין i של השיא נכתב ל־b במקום k. שימו לב: ל־b נכתבים מציינים ולא ערכים, וערך ששווה לשיא הקודם אינו שיא חדש.
פתרון – טבלת מעקב (max, b[k] ו־k אחרי ביצוע גוף הלולאה):
| i | a[i] | a[i] > max ? | max | b[k] | k |
|---|---|---|---|---|---|
| (התחלה) | 4 | 0 | |||
| 1 | 7 | 7 > 4 כן | 7 | b[0] ← 1 | 1 |
| 2 | 7 | 7 > 7 לא | 7 | – | 1 |
| 3 | 2 | לא | 7 | – | 1 |
| 4 | 9 | 9 > 7 כן | 9 | b[1] ← 4 | 2 |
| 5 | 5 | לא | 9 | – | 2 |
| 6 | 12 | 12 > 9 כן | 12 | b[2] ← 6 | 3 |
| 7 | 12 | 12 > 12 לא | 12 | – | 3 |
| 8 | הלולאה מסתיימת (8 < 8 לא מתקיים) | 12 | 3 |
המערך b לאחר ביצוע קטע התכנית:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| b | 1 | 4 | 6 | 0 | 0 | 0 | 0 |
בדיקה: על המערכים הנתונים מתקבל b = 1 4 6 0 0 0 0, k = 3 ו־max = 12.
טעות נפוצה: לכתוב ל־b את הערכים 7, 9, 12 במקום המציינים 1, 4, 6, או לספור את 7 ואת 12 פעמיים – ההשוואה היא "גדול ממש", ולכן ערך שווה לשיא אינו נכתב.
סעיף 2
למשל a = {5, 3, 5, 1, 4, 2, 5, 9} – כל מערך שבו a[1]…a[6] ≤ a[0] ו־a[7] > a[0].
מה נדרש: מערך a בגודל 8 עם מספרים גדולים מ־0, שבעבורו בסוף הביצוע b[0] = 7 וכל שאר תאי b הם 0.
רעיון: ל־b נכתבים מציינים i בין 1 ל־7, כלומר ערכים שונים מ־0. לכן:
מכאן: a[1], …, a[6] לא גדולים מ־a[0] (אף אחד מהם אינו שיא חדש), ו־a[7] גדול ממש מ־a[0].
פתרון: דוגמה מייצגת:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| a | 5 | 3 | 5 | 1 | 4 | 2 | 5 | 9 |
(גם 8, 8, 8, 8, 8, 8, 8, 20 מתאים: ערכים ששווים ל־a[0] אינם שיא חדש.)
בדיקה: בשתי הדוגמאות מתקבל b = 7 0 0 0 0 0 0 ו־k = 1. לעומת זאת, על 9, 1, 2, …, 7 (אין שיא חדש) התקבל b כולו אפסים, ועל 1, 2, …, 8 התקבל b = 1 2 3 4 5 6 7.
טעות נפוצה: לדרוש ש־a[0] יהיה הקטן ביותר. אז כמעט כל תא יהיה שיא חדש. צריך את ההפך: a[0] גדול או שווה לכל התאים שאחריו, חוץ מהאחרון.
שאלה 2 · לוח ההזמנות במטבח (20 נקודות)
במטבח של מסעדה מוצג על מסך "לוח ההזמנות הפתוחות". הלוח שמור במערך orders מטיפוס שלם בגודל 8 . לכל הזמנה יש מספר הזמנה חיובי, ומספרי ההזמנות במערך שונים זה מזה. ההזמנות שמורות לפי סדר הגעתן, החל מתא 0 , ואין ביניהן תאים ריקים. תא שאין בו הזמנה מכיל 0 , וכל התאים הריקים נמצאים בסוף המערך (ייתכן שאין במערך תאים ריקים כלל).
כאשר לקוח מבטל הזמנה, ההזמנה נמחקת מן הלוח: כל ההזמנות שאחריה זזות שמאלה כל אחת לתא שלידה, והתא האחרון במערך מקבל את הערך 0 .
כתבו פעולה בוליאנית חיצונית ששמה cancelOrder בשפת Java או CancelOrder בשפת C# , המקבלת את המערך orders ומספר הזמנה – num (מספר שלם חיובי).
אם ההזמנה num מופיעה במערך, הפעולה תמחק אותה כמתואר ותחזיר true . אחרת, המערך יישאר ללא שינוי ויוחזר false .
נתון המערך orders שלפניכם:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| orders | 104 | 117 | 121 | 98 | 130 | 0 | 0 | 0 |
דוגמה: עבור המערך orders ו־ num = 121 , הפעולה תחזיר true , והמערך ייראה כך בתום הפעולה:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| orders | 104 | 117 | 98 | 130 | 0 | 0 | 0 | 0 |
הסבר: ההזמנה 121 נמצאה באינדקס 2 . ההזמנות 98 ו־ 130 (וגם התאים הריקים) זזו תא אחד שמאלה, והתא האחרון קיבל 0 .
דוגמה נוספת: עבור אותו המערך המקורי ו־ num = 200 , המערך יישאר ללא שינוי והפעולה תחזיר false , כי ההזמנה 200 אינה מופיעה בלוח.
כתבו פעולה בוליאנית חיצונית ששמה cancelOrder בשפת Java או CancelOrder בשפת C# , המקבלת את המערך orders ומספר הזמנה – num (מספר שלם חיובי).
אם ההזמנה num מופיעה במערך, הפעולה תמחק אותה כמתואר ותחזיר true . אחרת, המערך יישאר ללא שינוי ויוחזר false .
◂ פתרון מלא לשאלה 2
פתרון
חיפוש סדרתי של num; כשנמצא באינדקס i – הזזה שמאלה orders[j] = orders[j+1] עבור j מ־ i עד length-2 , orders[length-1] = 0 , return true; אם לא נמצא – return false.
מה נדרש: למצוא את ההזמנה num ולמחוק אותה על ידי הזזה שמאלה של כל מה שאחריה; להחזיר אם ההזמנה נמצאה.
רעיון:
orders[j] = orders[j+1] עבור j מ־ i עד orders.length-2 . הערך שבתא i נדרס – וזו המחיקה.פתרון:
public static boolean cancelOrder(int[] orders, int num)
{
for (int i = 0; i < orders.length; i++)
{
if (orders[i] == num)
{
for (int j = i; j < orders.length - 1; j++)
{
orders[j] = orders[j + 1];
}
orders[orders.length - 1] = 0;
return true;
}
}
return false;
}בדיקה:
104 117 98 130 0 0 0 0 – כמו בדוגמה.104 117 121 98 130 0 0 0 .117 121 98 130 0 0 0 0 .5 6 7 8 9 10 11 12 : num = 12 (בתא האחרון) → 5 6 7 8 9 10 11 0 ; num = 5 → 6 7 8 9 10 11 12 0 .טעות נפוצה: להזיז מהסוף להתחלה (for j = length-2 ... i) – כך הערך של התא האחרון מועתק לכל התאים מ־ i והלאה. בהזזה שמאלה חייבים להתחיל מתא i ולהתקדם. טעות נוספת: לשכוח לשים 0 בתא האחרון – בלוח מלא ההזמנה האחרונה תופיע אז פעמיים.
public static boolean cancelOrder(int[] orders, int num)
{
for (int i = 0; i < orders.length; i++)
{
if (orders[i] == num)
{
for (int j = i; j < orders.length - 1; j++)
{
orders[j] = orders[j + 1];
}
orders[orders.length - 1] = 0;
return true;
}
}
return false;
}שאלה 3 · מספר הר – ספרות עולות ואחר כך יורדות (20 נקודות)
מספר שלם חיובי – num ייקרא "מספר הר" אם יש בו לפחות 3 ספרות, וכאשר קוראים את ספרותיו משמאל לימין:
הפסגה אינה הספרה הראשונה במספר ואינה הספרה האחרונה בו.
דוגמאות ל"מספר הר":
דוגמאות למספרים שאינם "מספר הר":
כתבו פעולה חיצונית ששמה isMountain בשפת Java או IsMountain בשפת C#, המקבלת מספר מטיפוס שלם – num הגדול מ־ 0 . הפעולה מחזירה true אם המספר הוא "מספר הר", אחרת היא מחזירה false .
כתבו פעולה חיצונית ששמה printMountains בשפת Java או PrintMountains בשפת C#, המקבלת מספר מטיפוס שלם – n הגדול מ־ 0 . הפעולה מדפיסה את כל "מספרי ההר" שיש מ־ 1 ועד n (כולל), ובסוף מדפיסה כמה "מספרי הר" נמצאו.
הערה: יש להשתמש בפעולה שכתבתם בסעיף א.
◂ פתרון מלא לשאלה 3
סעיף א
עוברים על הספרות מימין לשמאל (% 10, / 10). מימין לשמאל "מספר הר" נראה כך: קודם ספרות שגדלות (צלע הירידה), ואחר כך ספרות שקטנות (צלע העלייה). שני דגלים up ו-down; ספרה שווה, קטנה לפני כל גדלה, או גדלה אחרי קטנה ← false; בסוף מחזירים up && down.
מה נדרש
פעולה בוליאנית שבודקת אם ספרות num (משמאל לימין) עולות ממש עד פסגה ואחר כך יורדות ממש, עם לפחות ספרה אחת בכל צד של הפסגה.
רעיון
את הספרות קל לקבל מימין לשמאל (בעזרת % 10 ו-/ 10). קריאה מימין לשמאל של "מספר הר" נראית כך: קודם הספרות גדלות (זו צלע הירידה, הפוכה), ואחר כך הספרות קטנות (זו צלע העלייה, הפוכה).
שומרים את הספרה הקודמת prev ושני דגלים:
לכל ספרה d (משמאל ל-prev):
בסוף המספר הוא "מספר הר" רק אם ראינו גם עלייה וגם ירידה: up && down. (מספר עם ספרה אחת או שתיים לא יכול לקבל את שני הדגלים, ולכן התנאי "לפחות 3 ספרות" מתקיים מעצמו.)
פתרון
Java
public static boolean isMountain(int num)
{
boolean up = false; // ראינו ספרה גדולה מזו שמימינה (צלע הירידה של ההר)
boolean down = false; // ראינו ספרה קטנה מזו שמימינה (צלע העלייה של ההר)
int prev = num % 10; // הספרה הימנית ביותר
num = num / 10;
while (num > 0)
{
int d = num % 10; // הספרה שמשמאל ל-prev
if (d > prev)
{
if (down)
return false; // משמאל לימין: ירידה ואחריה שוב עלייה – יותר מפסגה אחת
up = true;
}
else if (d < prev)
{
if (!up)
return false; // אין צלע ירידה בצד ימין
down = true;
}
else
{
return false; // שתי ספרות סמוכות שוות
}
prev = d;
num = num / 10;
}
return up && down;
}C#
public static bool IsMountain(int num)
{
bool up = false; // ראינו ספרה גדולה מזו שמימינה (צלע הירידה של ההר)
bool down = false; // ראינו ספרה קטנה מזו שמימינה (צלע העלייה של ההר)
int prev = num % 10; // הספרה הימנית ביותר
num = num / 10;
while (num > 0)
{
int d = num % 10; // הספרה שמשמאל ל-prev
if (d > prev)
{
if (down)
return false; // משמאל לימין: ירידה ואחריה שוב עלייה – יותר מפסגה אחת
up = true;
}
else if (d < prev)
{
if (!up)
return false; // אין צלע ירידה בצד ימין
down = true;
}
else
{
return false; // שתי ספרות סמוכות שוות
}
prev = d;
num = num / 10;
}
return up && down;
}בדיקה
מעקב על num = 1352:
| d | prev | השוואה | up | down |
|---|---|---|---|---|
| (התחלה) | 2 | – | false | false |
| 5 | 2 | d > prev | true | false |
| 3 | 5 | d < prev, up = true | true | true |
| 1 | 3 | d < prev | true | true |
בסוף up && down = true ✓
מעקב על num = 1323: prev = 3, הספרה הבאה d = 2 < 3 אבל up עדיין false ← false ✓
1352, 12321, 120, 13531 ← true; 1233, 5310, 1357, 1323, 7, 98, 2552 ← false.
טעות נפוצה: לבדוק רק "יש עלייה ויש ירידה" בלי לוודא את הסדר – כך 1323 (עלייה, ירידה, עלייה) היה מתקבל בטעות. טעות נוספת: לשכוח את המקרה של ספרות שוות (1233).
public static boolean isMountain(int num)
{
boolean up = false; // ראינו ספרה גדולה מזו שמימינה (צלע הירידה של ההר)
boolean down = false; // ראינו ספרה קטנה מזו שמימינה (צלע העלייה של ההר)
int prev = num % 10; // הספרה הימנית ביותר
num = num / 10;
while (num > 0)
{
int d = num % 10; // הספרה שמשמאל ל-prev
if (d > prev)
{
if (down)
return false; // משמאל לימין: ירידה ואחריה שוב עלייה – יותר מפסגה אחת
up = true;
}
else if (d < prev)
{
if (!up)
return false; // אין צלע ירידה בצד ימין
down = true;
}
else
{
return false; // שתי ספרות סמוכות שוות
}
prev = d;
num = num / 10;
}
return up && down;
}סעיף ב
לולאה i = 1..n עם מונה count: אם isMountain(i) – מדפיסים i ומגדילים את count; אחרי הלולאה מדפיסים את count. למשל printMountains(140) מדפיסה 120, 121, 130, 131, 132, 140 ואחר כך את השורה count: 6.
מה נדרש
להדפיס את כל "מספרי ההר" מ-1 עד n, ובסוף את מספרם, תוך שימוש ב-isMountain מסעיף א.
רעיון
לולאה על i מ-1 עד n (כולל) עם מונה count שמתחיל ב-0. לכל i קוראים ל-isMountain(i); אם התשובה true – מדפיסים את i ומגדילים את count. אחרי הלולאה מדפיסים את count.
פתרון
Java
public static void printMountains(int n)
{
int count = 0;
for (int i = 1; i <= n; i++)
{
if (isMountain(i))
{
System.out.println(i);
count++;
}
}
System.out.println("count: " + count);
}C#
public static void PrintMountains(int n)
{
int count = 0;
for (int i = 1; i <= n; i++)
{
if (IsMountain(i))
{
Console.WriteLine(i);
count++;
}
}
Console.WriteLine("count: " + count);
}בדיקה
printMountains(140) מדפיסה (כל מספר בשורה נפרדת):
120, 121, 130, 131, 132, 140 ואחריהם count: 6.
(למשל 122 אינו "מספר הר" – הספרות 2, 2 שוות; 123 אינו – אין ירידה.) התוצאה זהה לבדיקה עצמאית שכתבנו בנפרד, וגם הספירה עד 99999 (6198 מספרים) זהה.
טעות נפוצה: להדפיס את count בתוך הלולאה – כך הוא מודפס שוב ושוב במקום פעם אחת בסוף.
public static void printMountains(int n)
{
int count = 0;
for (int i = 1; i <= n; i++)
{
if (isMountain(i))
{
System.out.println(i);
count++;
}
}
System.out.println("count: " + count);
}פרק שני (60 נקודות)
ענו על שתיים מן השאלות 4–6 (לכל שאלה – 30 נקודות).
שאלה 4 · סיורים במוזיאון – Tour ומערך של סיורים (30 נקודות)
מוזיאון הטבע מציע לקבוצות מבקרים סיורים מודרכים. כל סיור מתקיים בעברית או באנגלית.
נתונה המחלקה Tour – סיור, ולה ארבע תכונות:
הניחו שקיימות פעולות get/Get לתכונות המחלקה.
כתבו פעולה בונה במחלקה Tour , המקבלת את שם המדריך – guide , את מספר המבקרים – visitors , ואת החודש – month .
הפעולה תאתחל את תכונות המחלקה. בשפת הסיור (hebrew) הפעולה תיצור סיור בעברית.
הניחו שהפרמטרים שמתקבלים תקינים.
כתבו פעולה בונה במחלקה Tour המקבלת רק את החודש – month .
הפעולה תאתחל את תכונות המחלקה כך שיתקבל סיור באנגלית בהדרכת Maya , בחודש month , עם קבוצה מלאה של 25 מבקרים.
הערה: אם הפרמטר month אינו תקין, התכונה תקבל 7 (חודש יולי).
נתון מערך – arr , מטיפוס Tour , המכיל את כל הסיורים שהתקיימו במוזיאון במהלך שנה אחת. המערך אינו ממוין לפי סדר כלשהו, ואין בו ערכי null .
כתבו פעולה חיצונית ששמה avgVisitors בשפת Java או AvgVisitors בשפת C#, המקבלת את המערך arr , מספר מטיפוס שלם – month המציין חודש, ופרמטר בוליאני – hebrew .
אם hebrew הוא true , הפעולה תחזיר את ממוצע מספר המבקרים בסיור, מבין הסיורים בעברית שהתקיימו בחודש month .
אחרת (אם hebrew הוא false ), הפעולה תחזיר את ממוצע מספר המבקרים בסיור, מבין הסיורים באנגלית שהתקיימו בחודש month .
אם לא התקיים אף סיור מתאים, הפעולה תחזיר 0 .
הממוצע הוא מספר ממשי (double), ויכול להיות לא שלם.
הניחו שהפרמטר month שמתקבל תקין (בין 1 ל־ 12 ).
כתבו פעולה חיצונית ששמה onlyHebrew בשפת Java או OnlyHebrew בשפת C#, המקבלת את המערך arr .
הפעולה תדפיס את מספרי החודשים שבהם התקיים לפחות סיור אחד בעברית, ולא התקיים אף סיור באנגלית.
הערה: אפשר להיעזר בפעולה שכתבתם בתת־סעיף ב(1).
◂ פתרון מלא לשאלה 4
סעיף א(1)
this.guide = guide; this.visitors = visitors; this.month = month; this.hebrew = true;
מה נדרש
פעולה בונה עם שלושה פרמטרים שמאתחלת את ארבע התכונות; hebrew מקבלת תמיד true (סיור בעברית).
רעיון
מציבים כל פרמטר בתכונה המתאימה. התכונה hebrew אינה פרמטר – מציבים בה true. אין צורך לבדוק תקינות.
פתרון
כותרת המחלקה והתכונות:
Java
public class Tour
{
private String guide; // שם המדריך
private boolean hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)C#
public class Tour
{
private string guide; // שם המדריך
private bool hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)הפעולה הבונה:
Java
// סעיף א(1)
public Tour(String guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}C#
// סעיף א(1)
public Tour(string guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}המחלקה המלאה – בלשונית הקוד.
בדיקה
new Tour("Eli", 18, 3) ← guide = "Eli", hebrew = true, visitors = 18, month = 3.
public class Tour
{
private String guide; // שם המדריך
private boolean hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)
// סעיף א(1)
public Tour(String guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}
// פעולה בונה מלאה – לצורך הבדיקה בלבד
public Tour(String guide, boolean hebrew, int visitors, int month)
{
this.guide = guide;
this.hebrew = hebrew;
this.visitors = visitors;
this.month = month;
}
public String getGuide() { return this.guide; }
public boolean getHebrew() { return this.hebrew; }
public int getVisitors() { return this.visitors; }
public int getMonth() { return this.month; }
}סעיף א(2)
guide = "Maya", hebrew = false, visitors = 25, month = (1 ≤ month ≤ 12 ? month : 7).
מה נדרש
פעולה בונה נוספת עם פרמטר אחד (month) שיוצרת סיור באנגלית של Maya עם 25 מבקרים.
רעיון
פתרון
Java
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}C#
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}המחלקה המלאה כפי שהודרה ונבדקה (הפעולה הבונה בעלת ארבעת הפרמטרים נוספה לצורך הבדיקה בלבד):
Java
public class Tour
{
private String guide; // שם המדריך
private boolean hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)
// סעיף א(1)
public Tour(String guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}
// פעולה בונה מלאה – לצורך הבדיקה בלבד
public Tour(String guide, boolean hebrew, int visitors, int month)
{
this.guide = guide;
this.hebrew = hebrew;
this.visitors = visitors;
this.month = month;
}
public String getGuide() { return this.guide; }
public boolean getHebrew() { return this.hebrew; }
public int getVisitors() { return this.visitors; }
public int getMonth() { return this.month; }
}C#
public class Tour
{
private string guide; // שם המדריך
private bool hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)
// סעיף א(1)
public Tour(string guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}
// פעולה בונה מלאה – לצורך הבדיקה בלבד
public Tour(string guide, bool hebrew, int visitors, int month)
{
this.guide = guide;
this.hebrew = hebrew;
this.visitors = visitors;
this.month = month;
}
public string GetGuide() { return this.guide; }
public bool GetHebrew() { return this.hebrew; }
public int GetVisitors() { return this.visitors; }
public int GetMonth() { return this.month; }
}בדיקה
טעות נפוצה: להציב 7 תמיד, או להציב את month בלי בדיקה. לפי ההערה, 7 מוצב רק כשהערך שהתקבל אינו בין 1 ל-12.
public class Tour
{
private String guide; // שם המדריך
private boolean hebrew; // true = סיור בעברית, false = סיור באנגלית
private int visitors; // מספר המבקרים בסיור (1–25)
private int month; // החודש שבו התקיים הסיור (1–12)
// סעיף א(1)
public Tour(String guide, int visitors, int month)
{
this.guide = guide;
this.visitors = visitors;
this.month = month;
this.hebrew = true; // סיור בעברית
}
// סעיף א(2)
public Tour(int month)
{
this.guide = "Maya";
this.hebrew = false; // סיור באנגלית
this.visitors = 25; // קבוצה מלאה
if (month >= 1 && month <= 12)
this.month = month;
else
this.month = 7; // יולי
}
// פעולה בונה מלאה – לצורך הבדיקה בלבד
public Tour(String guide, boolean hebrew, int visitors, int month)
{
this.guide = guide;
this.hebrew = hebrew;
this.visitors = visitors;
this.month = month;
}
public String getGuide() { return this.guide; }
public boolean getHebrew() { return this.hebrew; }
public int getVisitors() { return this.visitors; }
public int getMonth() { return this.month; }
}סעיף ב(1)
סוכמים ומונים את הסיורים שבהם getMonth() == month וגם getHebrew() == hebrew; מחזירים (double) sum / count, או 0 אם count == 0.
מה נדרש
לחשב ממוצע (מספר ממשי) של מספר המבקרים בסיורים של חודש מסוים ובשפה מסוימת, ולהחזיר 0 כשאין סיורים כאלה.
רעיון
סריקה של המערך עם שני צוברים: sum (סך המבקרים) ו-count (מספר הסיורים המתאימים). סיור מתאים אם getMonth() == month וגם getHebrew() == hebrew. בסוף: אם count == 0 מחזירים 0 (כדי לא לחלק באפס), אחרת מחזירים sum / count – עם המרה ל-double כדי שהחלוקה לא תהיה חלוקת שלמים.
פתרון
Java
// סעיף ב(1)
public static double avgVisitors(Tour[] arr, int month, boolean hebrew)
{
int sum = 0;
int count = 0;
for (int i = 0; i < arr.length; i++)
{
if (arr[i].getMonth() == month && arr[i].getHebrew() == hebrew)
{
sum = sum + arr[i].getVisitors();
count++;
}
}
if (count == 0)
{
return 0;
}
return (double) sum / count;
}C#
// סעיף ב(1)
public static double AvgVisitors(Tour[] arr, int month, bool hebrew)
{
int sum = 0;
int count = 0;
for (int i = 0; i < arr.Length; i++)
{
if (arr[i].GetMonth() == month && arr[i].GetHebrew() == hebrew)
{
sum = sum + arr[i].GetVisitors();
count++;
}
}
if (count == 0)
{
return 0;
}
return (double) sum / count;
}בדיקה
מערך בדיקה (מדריך, שפה, מבקרים, חודש): (Eli, עברית, 20, 1), (Dana, עברית, 15, 1), new Tour(1), (Eli, עברית, 12, 2), (Dana, עברית, 9, 2), (Eli, עברית, 10, 3), (Dana, אנגלית, 6, 3), (Eli, אנגלית, 9, 3), new Tour(4), (Dana, עברית, 25, 8), new Tour(50).
(new Tour(m) הוא סיור באנגלית עם 25 מבקרים; new Tour(50) מתקיים בחודש 7.)
טעות נפוצה: לכתוב sum / count בלי המרה – בחלוקת שלמים (12 + 9) / 2 נותן 10 ולא 10.5. וגם לשכוח את המקרה count == 0, שגורם לחלוקה באפס.
// סעיף ב(1)
public static double avgVisitors(Tour[] arr, int month, boolean hebrew)
{
int sum = 0;
int count = 0;
for (int i = 0; i < arr.length; i++)
{
if (arr[i].getMonth() == month && arr[i].getHebrew() == hebrew)
{
sum = sum + arr[i].getVisitors();
count++;
}
}
if (count == 0)
{
return 0;
}
return (double) sum / count;
}סעיף ב(2)
לכל m מ-1 עד 12: אם avgVisitors(arr, m, true) > 0 וגם avgVisitors(arr, m, false) == 0 – מדפיסים את m.
מה נדרש
להדפיס כל חודש שבו היו סיורים בעברית בלבד (לפחות אחד), ואף לא סיור אחד באנגלית.
רעיון
בכל סיור יש לפחות מבקר אחד (visitors ≥ 1), ולכן הממוצע מסעיף ב(1) גדול מ-0 אם ורק אם התקיים לפחות סיור אחד מתאים, והוא שווה 0 בדיוק כשאין סיור כזה. לכן עוברים על החודשים 1–12 ובודקים: ממוצע בעברית > 0 וגם ממוצע באנגלית == 0.
פתרון
Java
// סעיף ב(2)
public static void onlyHebrew(Tour[] arr)
{
for (int m = 1; m <= 12; m++)
{
if (avgVisitors(arr, m, true) > 0 && avgVisitors(arr, m, false) == 0)
{
System.out.println(m);
}
}
}C#
// סעיף ב(2)
public static void OnlyHebrew(Tour[] arr)
{
for (int m = 1; m <= 12; m++)
{
if (AvgVisitors(arr, m, true) > 0 && AvgVisitors(arr, m, false) == 0)
{
Console.WriteLine(m);
}
}
}בדיקה
על מערך הבדיקה מסעיף ב(1):
הפלט: 2, 8 – כל אחד בשורה נפרדת.
טעות נפוצה: לבדוק רק שהממוצע באנגלית הוא 0 – אז יודפסו גם חודשים שלא התקיים בהם שום סיור (למשל חודש 5).
// סעיף ב(2)
public static void onlyHebrew(Tour[] arr)
{
for (int m = 1; m <= 12; m++)
{
if (avgVisitors(arr, m, true) > 0 && avgVisitors(arr, m, false) == 0)
{
System.out.println(m);
}
}
}שאלה 5 · רכבות ושעות יציאה – מחלקה מורכבת (30 נקודות)
נתונה המחלקה Time – שעה ביום, ולה שתי תכונות:
הניחו שקיימות פעולות get/Get ו־ set/Set לתכונות המחלקה.
בשאלה זו כל השעות הן באותו יום.
כתבו במחלקה Time פעולה פנימית ששמה minutesBetween בשפת Java או MinutesBetween בשפת C# המקבלת עצם אחר – other מטיפוס Time .
הפעולה מחזירה את מספר הדקות שבין שתי השעות (מספר שאינו שלילי, בלי קשר לשאלה איזו מן השעות מוקדמת יותר).
למשל, בין 8:55 ל־ 9:02 יש 7 דקות.
בתחנת רכבת מרכזית פיתחו מערכת שבודקת את לוח הזמנים של הרכבות היוצאות מן התחנה. לשם כך נבנתה המחלקה Train – רכבת, ולה שלוש תכונות:
הניחו שקיימות פעולות get/Get ו־ set/Set לתכונות המחלקה.
כתבו במחלקה Train פעולה פנימית ששמה samePlatform בשפת Java או SamePlatform בשפת C# , המקבלת עצם אחר – other מטיפוס Train . הפעולה מחזירה true אם שתי הרכבות יוצאות מאותו רציף. אחרת הפעולה מחזירה false .
במערכת זו, שתי רכבות נמצאות "בהתנגשות" אם גם הן יוצאות מאותו רציף וגם ההפרש בין שעות היציאה שלהן קטן מ־ 4 דקות (אם רק תנאי אחד מתקיים, הרכבות אינן בהתנגשות).
כתבו במחלקה Train פעולה פנימית ששמה tooClose בשפת Java או TooClose בשפת C# , המקבלת עצם אחר – other מטיפוס Train . הפעולה מחזירה true אם שתי הרכבות נמצאות "בהתנגשות", אחרת הפעולה מחזירה false .
יש להשתמש בפעולות שכתבתם בסעיף א ובתת־סעיף ב(1).
כתבו פעולה חיצונית ששמה countConflicts בשפת Java או CountConflicts בשפת C# המקבלת מערך – trains מטיפוס Train של כל הרכבות היוצאות מן התחנה ביום מסוים. המערך אינו מסודר לפי סדר כלשהו ואין בו ערכי null . הפעולה תחזיר את מספר הזוגות של רכבות שנמצאות "בהתנגשות" (כל זוג נספר פעם אחת בלבד).
למשל, אם במערך יש שלוש רכבות בלבד, וכולן יוצאות מרציף 1 בשעות 7:00 , 7:02 ו־ 7:05 – הפעולה תחזיר 2 (הזוג של 7:00 ו־ 7:02 והזוג של 7:02 ו־ 7:05; בין 7:00 ל־ 7:05 יש 5 דקות).
יש להשתמש בפעולה שכתבתם בתת־סעיף ב(2).
◂ פתרון מלא לשאלה 5
סעיף א
return Math.abs((hour * 60 + minute) - (other.getHour() * 60 + other.getMinute()));
מה נדרש: פעולה פנימית ב־ Time המחזירה את ההפרש בדקות בין שתי שעות.
רעיון: ממירים כל שעה למספר הדקות מתחילת היום: hour * 60 + minute. ההפרש בין שני המספרים הוא ההפרש בדקות, ו־ Math.abs (ב־ C#: Math.Abs) הופך אותו לאי־שלילי. את התכונות של other קוראים בפעולות get.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: 8:55 מול 9:02 → 7 (535 ו־ 542 דקות); 9:02 מול 8:55 → 7; 14:30 מול 14:30 → 0; 0:00 מול 23:59 → 1439.
טעות נפוצה: לחסר בנפרד שעות ודקות: (9 − 8) ו־ (2 − 55) – ההפרש בדקות יוצא שלילי ומבלבל. ההמרה לדקות פותרת זאת.
public int minutesBetween(Time other)
{
int mine = this.hour * 60 + this.minute;
int his = other.getHour() * 60 + other.getMinute();
return Math.abs(mine - his);
}סעיף ב(1)
return platform == other.getPlatform();
מה נדרש: פעולה פנימית ב־ Train הבודקת אם שתי רכבות יוצאות מאותו רציף.
רעיון: platform הוא מספר שלם, ולכן משווים אותו ישירות עם == ל־ other.getPlatform().
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: רציף 2 מול רציף 2 → true; רציף 2 מול רציף 3 → false.
public boolean samePlatform(Train other)
{
return this.platform == other.getPlatform();
}סעיף ב(2)
return samePlatform(other) && departure.minutesBetween(other.getDeparture()) < 4;
מה נדרש: פעולה tooClose המחזירה true רק אם הרכבות יוצאות מאותו רציף וגם ההפרש בין שעות היציאה קטן מ־ 4 דקות.
רעיון: זו מחלקה מורכבת – התכונה departure היא עצם מטיפוס Time. לכן את חישוב ההפרש מעבירים לפעולה minutesBetween של העצם departure:
this.departure.minutesBetween(other.getDeparture()), ומחברים עם samePlatform(other) ב־ &&.
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: רציף 2, 8:58 מול רציף 2, 9:01 (3 דקות) → true; רציף 2, 8:58 מול רציף 2, 9:02 (4 דקות) → false (4 אינו קטן מ־ 4); רציף 2 מול רציף 3 באותה שעה → false.
טעות נפוצה: לכתוב ≤ 4 במקום < 4 – "קטן מ־ 4 דקות" אינו כולל הפרש של 4 דקות בדיוק.
public boolean tooClose(Train other)
{
return this.samePlatform(other) &&
this.departure.minutesBetween(other.getDeparture()) < 4;
}סעיף ג
לולאה כפולה i < j: אם trains[i].tooClose(trains[j]) – count++; מחזירים count
מה נדרש: לספור את כל הזוגות השונים של רכבות שנמצאות בהתנגשות.
רעיון: לולאה כפולה על כל הזוגות: i מ־ 0, ו־ j מ־ i + 1. כך כל זוג {i, j} נבדק פעם אחת בדיוק, ורכבת אינה מושווית לעצמה. לכל זוג שבו trains[i].tooClose(trains[j]) מגדילים מונה.
(ההתנגשות סימטרית – אם i מתנגשת ב־ j גם j מתנגשת ב־ i – ולכן אין צורך לבדוק את הזוג בשני הכיוונים.)
פתרון: הקוד המלא – בלשונית Java / C#.
בדיקה: הדוגמה מהשאלה → 2. מערך של 6 רכבות: A רציף 1 7:00, B רציף 1 7:02, C רציף 1 7:05, D רציף 2 7:03, E רציף 2 8:00, F רציף 1 7:03. הזוגות בהתנגשות: A–B (2 דק'), A–F (3), B–C (3), B–F (1), C–F (2) → הוחזר 5. מערך בלי התנגשויות → 0.
טעות נפוצה: להריץ את j מ־ 0 עם התנאי i != j – כך כל זוג נספר פעמיים (התוצאה בדוגמה תהיה 4 במקום 2).
public static int countConflicts(Train[] trains)
{
int count = 0;
for (int i = 0; i < trains.length; i++)
{
for (int j = i + 1; j < trains.length; j++)
{
if (trains[i].tooClose(trains[j]))
count++;
}
}
return count;
}שאלה 6 · כיווץ מסר ושחזורו
כדי לחסוך מקום, אפליקציית מסרים מסוימת "מכווצת" כל מסר: כל רצף של תווים זהים הסמוכים זה לזה מוחלף בתו אחד בלבד.
לדוגמה: לאחר כיווץ המילה bookkeeper מתקבל המסר המכווץ bokeper .
כדי שמקבל המסר יוכל לשחזר את המסר המקורי, מצורף למסר המכווץ "מערך מונים". גודל מערך המונים הוא כאורך המסר המכווץ, ובכל תא מופיע מספר הפעמים שהתו המתאים במסר המכווץ הופיע ברצף במסר המקורי.
לדוגמה: עבור המסר המכווץ bokeper (של המסר המקורי bookkeeper), מערך המונים הוא:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| 1 | 2 | 2 | 2 | 1 | 1 | 1 |
הסבר: התו הראשון במסר המכווץ (b) הופיע במסר המקורי פעם אחת, התו השני במסר המכווץ (o) הופיע במסר המקורי פעמיים ברצף, התו השלישי במסר המכווץ (k) הופיע במסר המקורי פעמיים ברצף, וכן הלאה.
רשמו את המסר המכווץ ואת מערך המונים עבור המסר Mississippi .
כתבו פעולה חיצונית ששמה expand בשפת Java או Expand בשפת C# , המקבלת מחרוזת מכווצת – str ומערך מונים – arr . הפעולה תחזיר את המחרוזת המקורית.
◂ פתרון מלא לשאלה 6
סעיף א
המסר המכווץ: Misisipi ; מערך המונים: {1, 1, 2, 1, 2, 1, 2, 1}
מה נדרש: לכווץ את Mississippi ולרשום את מערך המונים.
רעיון: מחלקים את המסר לרצפים של תווים זהים סמוכים: M | i | ss | i | ss | i | pp | i. מכל רצף נשאר תו אחד, והמונה הוא אורך הרצף. (שימו לב: M גדולה ו־i קטנה – תווים שונים.)
פתרון: המסר המכווץ הוא Misisipi (8 תווים), ומערך המונים:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 1 | 2 | 1 | 2 | 1 |
בדיקה: סכום המונים 1+1+2+1+2+1+2+1 = 11 = אורך Mississippi. בתוכנית הבדיקה כיווצנו את Mississippi וקיבלנו Misisipi / 1 1 2 1 2 1 2 1, והפעולה מסעיף ב שחזרה ממנו את Mississippi.
טעות נפוצה: לספור את כל המופעים של תו במילה (למשל s מופיעה 4 פעמים) במקום את אורך הרצף; או למזג את כל ה־i למונה אחד – כל רצף נפרד מקבל תא משלו.
סעיף ב
לכל i משרשרים לתוצאה את str.charAt(i) בדיוק arr[i] פעמים (לולאה מקוננת).
מה נדרש: לבנות את המסר המקורי מהמסר המכווץ ומערך המונים.
רעיון: התו ה־i במסר המכווץ הופיע במקור arr[i] פעמים ברצף, ולפי הסדר. לכן עוברים על התווים לפי הסדר, ולכל תו מוסיפים אותו לתוצאה arr[i] פעמים בלולאה פנימית.
פתרון: הקוד המלא – בלשונית Java/C#.
בדיקה:
expand("bokeper", {1,2,2,2,1,1,1}) → bookkeeper (הדוגמה שבשאלה).expand("Misisipi", {1,1,2,1,2,1,2,1}) → Mississippi (סעיף א).expand("a", {5}) → aaaaa; expand("xyx", {1,3,1}) → xyyyx.טעות נפוצה: לכתוב result += str.charAt(i) * arr[i] – כפל של תו במספר נותן מספר ולא חזרה על התו; או לכתוב result += arr[i] ולשרשר את המספר עצמו.
public class Q6
{
public static String expand(String str, int[] arr)
{
String result = "";
for (int i = 0; i < str.length(); i++)
{
for (int k = 0; k < arr[i]; k++) // התו ה־i חוזר arr[i] פעמים
result += str.charAt(i);
}
return result;
}
}טבלת התשובות
| שאלה | סעיף | תשובה |
|---|---|---|
| 1 | 1 | b = {1, 4, 6, 0, 0, 0, 0} (k = 3, max = 12) |
| 2 | למשל a = {5, 3, 5, 1, 4, 2, 5, 9} – כל מערך שבו a[1]…a[6] ≤ a[0] ו־a[7] > a[0]. | |
| 2 | - | חיפוש סדרתי של num; כשנמצא באינדקס i – הזזה שמאלה orders[j] = orders[j+1] עבור j מ־ i עד length-2 , orders[length-1] = 0 , return true; אם לא נמצא – return false. |
| 3 | א | עוברים על הספרות מימין לשמאל (% 10, / 10). מימין לשמאל "מספר הר" נראה כך: קודם ספרות שגדלות (צלע הירידה), ואחר כך ספרות שקטנות (צלע העלייה). שני דגלים up ו-down; ספרה שווה, קטנה לפני כל גדלה, או גדלה אחרי קטנה ← false; בסוף מחזירים up && down. |
| ב | לולאה i = 1..n עם מונה count: אם isMountain(i) – מדפיסים i ומגדילים את count; אחרי הלולאה מדפיסים את count. למשל printMountains(140) מדפיסה 120, 121, 130, 131, 132, 140 ואחר כך את השורה count: 6. | |
| 4 | א(1) | this.guide = guide; this.visitors = visitors; this.month = month; this.hebrew = true; |
| א(2) | guide = "Maya", hebrew = false, visitors = 25, month = (1 ≤ month ≤ 12 ? month : 7). | |
| ב(1) | סוכמים ומונים את הסיורים שבהם getMonth() == month וגם getHebrew() == hebrew; מחזירים (double) sum / count, או 0 אם count == 0. | |
| ב(2) | לכל m מ-1 עד 12: אם avgVisitors(arr, m, true) > 0 וגם avgVisitors(arr, m, false) == 0 – מדפיסים את m. | |
| 5 | א | return Math.abs((hour * 60 + minute) - (other.getHour() * 60 + other.getMinute())); |
| ב(1) | return platform == other.getPlatform(); | |
| ב(2) | return samePlatform(other) && departure.minutesBetween(other.getDeparture()) < 4; | |
| ג | לולאה כפולה i < j: אם trains[i].tooClose(trains[j]) – count++; מחזירים count | |
| 6 | א | המסר המכווץ: Misisipi ; מערך המונים: {1, 1, 2, 1, 2, 1, 2, 1} |
| ב | לכל i משרשרים לתוצאה את str.charAt(i) בדיוק arr[i] פעמים (לולאה מקוננת). |