נסו בחינם — מדעי המחשב
חינםמכל נושא: שלושה תרגילי בנק עם רמז ופתרון והחלק הראשון של החוברת. הפריטים האלה מסומנים בחינם, ושאר החומר פתוח.
לעמוד המקצועתרגילים
מערך חד-ממדי
פתרון
רמת קושי: קלה סוג: מציאת פלט האינדקס הראשון הוא 0 והאחרון הוא length - 1, כלומר 3. הערך שם 9. האורך 4. | פלט | | 4 | | 9 | | 4 | הפלט הסופי: 4 9 4
פתרון
רמת קושי: קלה סוג: מציאת פלט 2 + 5 + 3 = 10. i מוכרז לפני הלולאה. | i | sum | | 0 | 2 | | 1 | 7 | | 2 | 10 | | פלט | 10 | הפלט הסופי: 10
פתרון
רמת קושי: קלה סוג: מציאת פלט מחליפים את 8 עם 6 בעזרת t. האמצע נשאר 1. | a[0] | a[1] | a[2] | | 6 | 1 | 8 | הפלט הסופי: 6 1 8
מערך דו-ממדי
פתרון
רמת קושי: קלה סוג: מציאת פלט שתי שורות, שלוש עמודות. m[1][2] הוא 6. | פלט | | 2 | | 3 | | 6 | הפלט הסופי: 2 3 6
פתרון
רמת קושי: קלה סוג: מציאת פלט 1+2+3+4 = 10. שני המונים מוכרזים לפני הלולאות. | sum | | 10 | הפלט הסופי: 10
פתרון
רמת קושי: קלה סוג: מציאת פלט משתנים 1 ו־4. 2 ו־3 נשארים. | מטריצה | | 2 2 | | 3 8 | הפלט הסופי: 2 2 3 8
עצים בינאריים
פתרון
רמת קושי: קלה סוג: מציאת פלט השורש 5, בנו השמאלי 2, ויש בן ימני. | שורש | שמאל | hasRight | | 5 | 2 | true | הפלט הסופי: 5 2 true
פתרון
רמת קושי: קלה סוג: מציאת פלט לצומת בודד אין בנים. | פלט | | עלה | | 7 | הפלט הסופי: עלה 7
פתרון
רמת קושי: קלה סוג: מציאת פלט שורש ושני עלים: 3. עץ ריק: 0. | עץ | גודל | | שלושה צמתים | 3 | | null | 0 | הפלט הסופי: 3 0
תנאי מורכב
פתרון
רמת קושי: קלה סוג: מציאת פלט שני החלקים אמת: 15 >= 10 וגם 15 <= 20. לכן "בטווח". | n | >= 10 | <= 20 | פלט | | 15 | אמת | אמת | בטווח | הפלט הסופי: בטווח
פתרון
רמת קושי: קלה סוג: מציאת פלט a נכשל אבל b עבר. ב־|| מספיק חלק אחד אמיתי, ולכן מודפס "לפחות אחד עבר". | a | b | פלט | | 40 | 80 | לפחות אחד עבר | הפלט הסופי: לפחות אחד עבר
פתרון
רמת קושי: קלה סוג: מציאת פלט x > 0 וגם x < 10 הוא אמת. השלילה של אמת היא שקר, ולכן ה־else מדפיס "כן". | x | תנאי פנימי | אחרי ! | פלט | | 3 | אמת | שקר | כן | הפלט הסופי: כן
תנאי פשוט
פתרון
רמת קושי: קלה סוג: מציאת פלט age הוא 17. התנאי 17 >= 18 שקרי, לכן מתבצעת ה־else ומודפס "קטין". | age | תנאי | פלט | | 17 | שקר | קטין | הפלט הסופי: קטין
פתרון
רמת קושי: קלה סוג: מציאת פלט 14 מתחלק ב־2 ללא שארית, לכן התנאי אמת ומודפס "זוגי". | n | n % 2 | פלט | | 14 | 0 | זוגי | הפלט הסופי: זוגי
פתרון
רמת קושי: קלה סוג: מציאת פלט 54 אינו גדול או שווה ל־55, ולכן מודפס "נכשל". | grade | תנאי | פלט | | 54 | שקר | נכשל | הפלט הסופי: נכשל
לולאת for
פתרון
רמת קושי: קלה סוג: מציאת פלט i רץ מ־1 עד 4 כולל, כי התנאי נבדק לפני כל סיבוב ו־4 <= 4. | i | פלט | | 1 | 1 | | 2 | 2 | | 3 | 3 | | 4 | 4 | | 5 | התנאי נכשל, היציאה | הפלט הסופי: 1 2 3 4
פתרון
רמת קושי: קלה סוג: מציאת פלט הלולאה רצה עבור 0, 1, 2, 3. הסכום 6. i מוכרז מחוץ ללולאה. | i | sum | | 0 | 0 | | 1 | 1 | | 2 | 3 | | 3 | 6 | | פלט | 6 | הפלט הסופי: 6
פתרון
רמת קושי: קלה סוג: מציאת פלט המונה גדל ב־2 בכל סיבוב: 2, 4, 6, 8. אחרי כל מספר מודפס רווח. אין ירידת שורה. פלט: 2 4 6 8 הפלט הסופי: 2 4 6 8
רשימה מקושרת
פתרון
רמת קושי: קלה סוג: מציאת פלט החוליה הראשונה מחזיקה 1 ומצביעה על 2. | ערך | הבא | hasNext | | 1 | 2 | true | הפלט הסופי: 1 2 true
פתרון
רמת קושי: קלה סוג: מציאת פלט שתי חוליות. p מוכרז לפני הלולאה. | n | | 2 | הפלט הסופי: 2
פתרון
רמת קושי: קלה סוג: מציאת פלט | חוליה | ערך חדש | | ראשונה | 1 | | שנייה | 2 | הפלט הסופי: 1 2
שימוש ב-Math
פתרון
רמת קושי: קלה סוג: מציאת פלט | פעולה | תוצאה | | Math.abs(-7) | 7 | | Math.max(7, 4) | 7 | הפלט הסופי: 7 7
פתרון
רמת קושי: קלה סוג: מציאת פלט קודם Math.min(2, 5) הוא 2, ואז Math.min(9, 2) הוא 2. | small | | 2 | הפלט הסופי: 2
פתרון
רמת קושי: קלה סוג: מציאת פלט השורש של 16 הוא 4.0, לא 4, כי הטיפוס הוא double. | root | פלט | | 4.0 | 4.0 | הפלט הסופי: 4.0
פעולות/מתודות
פתרון
רמת קושי: קלה סוג: מציאת פלט add מחזירה 4+7. | קריאה | פלט | | add(4, 7) | 11 | הפלט הסופי: 11
פתרון
רמת קושי: קלה סוג: מציאת פלט bump מקבלת עותק של 5 ומגדילה את העותק. n בקוראת נשאר 5. | n אחרי הקריאה | פלט | | 5 | 5 | הפלט הסופי: 5
פתרון
רמת קושי: קלה סוג: מציאת פלט קודם add(2, 1) מחזיר 3, ואז square(3) מחזיר 9. | שלב | ערך | | add | 3 | | square | 9 | הפלט הסופי: 9
מחלקות ועצמים
פתרון
רמת קושי: קלה סוג: מציאת פלט הבנאי שומר את הערכים בשדות הפרטיים. getX ו־getY מחזירים אותם. | getX | getY | | 2 | 5 | הפלט הסופי: 2 5
פתרון
רמת קושי: קלה סוג: מציאת פלט לכל עצם שדה משלו. a צובר 5, ו־b צובר 1. | a | b | | 5 | 1 | הפלט הסופי: 5 1
פתרון
רמת קושי: קלה סוג: מציאת פלט | x | y | | 1 | 1 | | 3 | -3 | הפלט הסופי: 3 -3
תור
פתרון
רמת קושי: קלה סוג: מציאת פלט הראשון שנכנס הוא הראשון שיוצא. | remove | פלט | | | 1 | | | 2 | | | 3 | הפלט הסופי: 1 2 3
פתרון
רמת קושי: קלה סוג: מציאת פלט head רואה את 4 בלי להוציא. remove הראשון גם הוא 4. | קריאה | פלט | | head | 4 | | head | 4 | | remove | 4 | הפלט הסופי: 4 4 4
פתרון
רמת קושי: קלה סוג: מציאת פלט | מצב | isEmpty | | בהתחלה | true | | אחרי insert | false | | אחרי remove | true | הפלט הסופי: true false true
רקורסיה
פתרון
רמת קושי: קלה סוג: מציאת פלט 4*3*2*1*1 = 24. הבסיס של 0 הוא 1. | קריאה | מוחזר | | factorial(0) | 1 | | factorial(4) | 24 | הפלט הסופי: 24 1
פתרון
רמת קושי: קלה סוג: מציאת פלט 5+4+3+2+1+0 = 15. | מוחזר | | 15 | הפלט הסופי: 15
פתרון
רמת קושי: קלה סוג: מציאת פלט ההדפסה לפני הקריאה, ולכן הסדר יורד. | פלט | | 3 | | 2 | | 1 | הפלט הסופי: 3 2 1
מחסנית
פתרון
רמת קושי: קלה סוג: מציאת פלט נכנסים 1 ואז 2 ואז 3. יוצאים בסדר הפוך. | פעולה | פלט | | pop | 3 | | pop | 2 | | pop | 1 | הפלט הסופי: 3 2 1
פתרון
רמת קושי: קלה סוג: מציאת פלט top מחזיר 7 בלי להוציא. לכן גם הקריאה השנייה היא 7, וה־pop הראשון גם הוא 7. | קריאה | פלט | | top | 7 | | top | 7 | | pop | 7 | הפלט הסופי: 7 7 7
פתרון
רמת קושי: קלה סוג: מציאת פלט | מצב | isEmpty | | בהתחלה | true | | אחרי push | false | | אחרי pop | true | הפלט הסופי: true false true
מחרוזות (String)
פתרון
רמת קושי: קלה סוג: מציאת פלט "שלום" הוא ארבעה תווים. התו הראשון הוא ש. | length | charAt(0) | | 4 | ש | הפלט הסופי: 4 ש
פתרון
רמת קושי: קלה סוג: מציאת פלט equals משווה תוכן ולכן true. == משווה הפניות, ושני new שונים, ולכן false. | equals | == | | true | false | הפלט הסופי: true false
פתרון
רמת קושי: קלה סוג: מציאת פלט מ־1 עד לפני 4: bcd. מ־3 עד הסוף: de. | פלט | | bcd | | de | הפלט הסופי: bcd de
לולאת while
פתרון
רמת קושי: קלה סוג: מציאת פלט | i בתחילת הסיבוב | פלט | i אחרי | | 1 | 1 | 2 | | 2 | 2 | 3 | | 3 | 3 | 4 | | 4 | יציאה | | הפלט הסופי: 1 2 3
פתרון
רמת קושי: קלה סוג: מציאת פלט חלוקת שלמים: 40 → 20 → 10 → 5. 5 אינו גדול מ־5, והלולאה נעצרת. | n | | 40 | | 20 | | 10 | | 5 | | פלט 5 | הפלט הסופי: 5
פתרון
רמת קושי: קלה סוג: מציאת פלט 2 > 10 שקרי מיד. steps נשאר 0 ו־n נשאר 2. | n | steps | פלט | | 2 | 0 | 0 | | 2 | 0 | 2 | הפלט הסופי: 0 2
תחילת החוברת
איבר מחליף כיוון הוא מספר בסדרת מספרים שבו משתנה סדר המיון בין איברים סמוכים – מעלייה לירידה או מירידה לעלייה. איבר מחליף כיוון שייך גם לרצף של מספרים הממוינים בסדר עולה וגם לרצף של מספרים הממוינים בסדר יורד.
לדוגמה: בסדרה 2 5 7 4 2 1 8 המספרים 7 ו-1 הם איברים מחליפי כיוון.
א. כתבו פעולה חיצונית (סטטית) שתקבל מחסנית לא ריקה של מספרים שלמים, שאין בה מספרים זהים צמודים זה לזה. הפעולה תכניס למחסנית אחרי כל איבר מחליף כיוון איבר זהה לו.
דוגמאות:
| ← ראש המחסנית | 2 |
| 1 | |
| 2 | |
| 3 | |
| 2 | |
| 1 |
| ← ראש המחסנית | 2 |
| 1 | |
| 1 | |
| 2 | |
| 3 | |
| 3 | |
| 2 | |
| 1 |
| ← ראש המחסנית | 3 |
| 7 | |
| 3 | |
| 7 | |
| 3 |
| ← ראש המחסנית | 3 |
| 7 | |
| 7 | |
| 3 | |
| 3 | |
| 7 | |
| 7 | |
| 3 |
| ← ראש המחסנית | 9 |
| 6 | |
| 5 | |
| 4 |
| ← ראש המחסנית | 9 |
| 6 | |
| 5 | |
| 4 |
ב. מהי סיבוכיות זמן הריצה של הפעולה שכתבתם? נמקו.
הערה: אפשר להשתמש בפעולות המחלקה Stack<T> בלי לממש אותן.
רעיון: מעבירים את האיברים מהמחסנית למחסנית עזר, ובכל שלב בודקים את האיבר הנוכחי מול הקודם לו (שכבר הוצא) ומול הבא אחריו (top). איבר שגדול משני שכניו או קטן משני שכניו – מחליף כיוון, ולכן דוחפים אותו פעמיים. האיבר הראשון והאחרון אינם מחליפי כיוון. בסוף מחזירים את האיברים למחסנית המקורית, כך שהסדר נשמר.
public static void doubleTurns(Stack<Integer> s)
{
int cur;
int next;
Stack<Integer> temp = new Stack<Integer>();
int prev = s.pop(); // האיבר הראשון – אינו מחליף כיוון
temp.push(prev);
while (!s.isEmpty())
{
cur = s.pop();
temp.push(cur);
if (!s.isEmpty()) // לאיבר האחרון אין שכן שני
{
next = s.top();
// מחליף כיוון: גדול משני שכניו או קטן משני שכניו
if ((cur > prev && cur > next) || (cur < prev && cur < next))
temp.push(cur); // מכניסים איבר זהה אחריו
}
prev = cur;
}
while (!temp.isEmpty()) // החזרה למחסנית המקורית – הסדר נשמר
s.push(temp.pop());
}ב. סיבוכיות: כל איבר מוצא פעם אחת למחסנית העזר ומוחזר פעם אחת (לכל היותר פעמיים בגלל ההכפלה), וכל פעולת מחסנית היא O(1). לכן הסיבוכיות היא O(n), כאשר n מספר האיברים במחסנית.
לפניכם פעולה המקבלת כפרמטר תור q של מספרים שלמים הממוינים בסדר עולה. כל המספרים שונים זה מזה. הניחו שבתור יש לפחות שני מספרים.
public static int what(Queue<Integer> q)
{
int x = q.remove();
int y = q.head();
q.insert(x);
if (x > y)
return 0;
return what(q) + (y - x);
}| ראש התור | 1 | 4 | 8 | 17 | 20 | סוף התור |
(1) עקבו אחר הפעולה what עבור התור q1 וכתבו את הערך שיוחזר. הראו את הערכים של x ושל y ואת התור לאחר כל זימון רקורסיבי.
(2) (i) מה מחזירה הפעולה עבור תור כלשהו של מספרים ממוינים בסדר עולה ושונים זה מזה? (ii) כיצד נראה התור לאחר סיום הפעולה?
(3) אם נחליף את התנאי if (x > y) בתנאי if (q.isEmpty()) תיווצר שגיאת זמן ריצה. מהי השגיאה? הסבירו.
(1) מעקב:
| זימון | x | y | התור אחרי insert(x) | מוחזר |
|---|---|---|---|---|
| 1 | 1 | 4 | 4,8,17,20,1 | what(...)+3 = 19 |
| 2 | 4 | 8 | 8,17,20,1,4 | what(...)+4 = 16 |
| 3 | 8 | 17 | 17,20,1,4,8 | what(...)+9 = 12 |
| 4 | 17 | 20 | 20,1,4,8,17 | what(...)+3 = 3 |
| 5 | 20 | 1 | 1,4,8,17,20 | 0 (כי 20 > 1) |
הפעולה מחזירה 19.
(2)(i) הפעולה מחזירה את ההפרש בין המספר הגדול ביותר לקטן ביותר בתור (האחרון פחות הראשון) – סכום ההפרשים הסמוכים "מתקפל".
(ii) התור חוזר למצבו המקורי – כל איבר הוצא מהראש והוכנס לסוף פעם אחת (סיבוב שלם).
(3) התור לעולם אינו מתרוקן (בכל זימון מוציאים איבר אחד ומכניסים אותו בחזרה), ולכן תנאי העצירה לא יתקיים לעולם – רקורסיה אינסופית שתסתיים בשגיאת גלישת מחסנית הזימונים (StackOverflowError).
לפניכם ממשק המחלקה "תור-ביטול" (UndoQueue). המחלקה מגדירה אוסף עם פרוטוקול FIFO להכנסה והוצאה של ערכים שלמים וגדולים מ-0.
| פעולה | תיאור |
|---|---|
| UndoQueue() | בונה תור-ביטול ריק |
| boolean isEmpty() | מחזירה true אם התור-ביטול ריק |
| void insert(int x) | מכניסה את x לסוף התור-ביטול |
| int remove() | מוציאה ומחזירה את הערך שבראש (הנחה: לא ריק) |
| int head() | מחזירה את הערך שבראש בלי להוציאו |
| void undo() | מבטלת את פעולת ה-remove או ה-insert האחרונה שבוצעה ומשחזרת את התור-ביטול כפי שהיה לפניה. אם אין פעולות כאלה – אינה עושה דבר. הפעלה n פעמים מבטלת את n הפעולות האחרונות שטרם בוטלו. |
א. הראו את מצב התור-ביטול לאחר כל הוראה בקטע הקוד:
UndoQueue q = new UndoQueue(); q.insert(1); q.insert(2); q.insert(3); q.remove(); q.insert(4); q.undo(); q.undo();
ב. כתבו ב-Java את כותרת המחלקה UndoQueue ואת התכונות שלה, עם תיעוד לכל תכונה.
ג. ממשו את הפעולות insert, remove ו-undo. אפשר להשתמש בפעולות הממשק האחרות בלי לממש אותן.
א. (התור מוצג מהראש לסוף)
| הוראה | מצב התור-ביטול |
|---|---|
| new UndoQueue() | ריק |
| insert(1) | 1 |
| insert(2) | 1, 2 |
| insert(3) | 1, 2, 3 |
| remove() | 2, 3 |
| insert(4) | 2, 3, 4 |
| undo() | 2, 3 (בוטל insert(4)) |
| undo() | 1, 2, 3 (בוטל remove – 1 חזר לראש) |
ב. תכונות: תור לערכים, ומחסנית של הפעולות שבוצעו – כי undo מבטל תמיד את הפעולה האחרונה (LIFO). מאחר שכל הערכים גדולים מ-0, נקודד: 0 = בוצע insert; x > 0 = בוצע remove שהוציא את x.
public class UndoQueue
{
private Queue<Integer> q; // ערכי התור-ביטול, לפי סדר FIFO
private Stack<Integer> ops; // היסטוריית פעולות: 0 = insert, x>0 = remove שהוציא את x
public UndoQueue()
{
q = new Queue<Integer>();
ops = new Stack<Integer>();
}
// ג.
public void insert(int x)
{
q.insert(x);
ops.push(0); // רישום: בוצעה הכנסה
}
public int remove()
{
int x = q.remove();
ops.push(x); // רישום: הוצא x
return x;
}
public void undo()
{
int x;
if (ops.isEmpty())
return; // אין מה לבטל
int op = ops.pop();
Queue<Integer> temp = new Queue<Integer>();
if (op == 0)
{
// ביטול insert: מוחקים את האיבר האחרון בתור
while (!q.isEmpty())
{
x = q.remove();
if (!q.isEmpty()) // x אינו האחרון – נשמר
temp.insert(x);
}
}
else
{
// ביטול remove: מחזירים את op לראש התור
temp.insert(op);
while (!q.isEmpty())
temp.insert(q.remove());
}
q = temp;
}
// isEmpty, head – לפי הממשק
}סיבוכיות: insert ו-remove – O(1); undo – O(n).
"שרשרת מספרים שלמים חיוביים" היא שרשרת חוליות שכל חוליה בה מכילה מספר שלם הגדול מ-0.
"שרשרת ספרות" היא שרשרת חוליות שכל חוליה בה מכילה ספרה בין 0 ל-9 או את המספר (-9). כל רצף ספרות בשרשרת מייצג מספר: הספרה הראשונה מייצגת את האחדות, השנייה את העשרות וכן הלאה. לאחר כל רצף ספרות מופיע (-9), המסמן סוף של מספר.
דוגמה ל"שרשרת ספרות" המייצגת את המספרים 92, 4, 543:
כתבו פעולה חיצונית buildDigit המקבלת הפניה lst שאינה null ל"שרשרת מספרים שלמים חיוביים". הפעולה תחזיר "שרשרת ספרות" המייצגת את המספרים שב-lst לפי הסדר.
רעיון: בונים שרשרת חדשה עם הפניה לחוליה האחרונה ("זנב") כדי להוסיף בסוף ב-O(1). חוליית דמה בתחילה חוסכת טיפול מיוחד בחוליה הראשונה.
public static Node<Integer> buildDigit(Node<Integer> lst)
{
int num;
Node<Integer> dummy = new Node<Integer>(0); // חוליית דמה
Node<Integer> last = dummy;
while (lst != null)
{
num = lst.getValue();
while (num > 0) // ספרות מהאחדות והלאה
{
last.setNext(new Node<Integer>(num % 10));
last = last.getNext();
num = num / 10;
}
last.setNext(new Node<Integer>(-9)); // סימון סוף מספר
last = last.getNext();
lst = lst.getNext();
}
return dummy.getNext();
}סיבוכיות: O(n·d), n – מספר החוליות, d – מספר הספרות המרבי (בפועל לינארי במספר הספרות הכולל).
א. ממשו את הפעולה public static void leaves(BinNode<Integer> t, Stack<Integer> s). הפעולה מקבלת עץ בינרי לא ריק t של מספרים שלמים, ומחסנית ריקה s. הפעולה מכניסה למחסנית את ערכי כל העלים של העץ t, על פי סדר סריקה מימין לשמאל.
ב. כתבו פעולה בוליאנית שתקבל 2 עצים בינריים לא ריקים של מספרים שלמים, ותחזיר true אם יש להם אותו מספר עלים, ועל פי סדר הסריקה מימין לשמאל ערכי העלים שווים. אחרת – false. עליכם להשתמש בפעולה מסעיף א.
// א.
public static void leaves(BinNode<Integer> t, Stack<Integer> s)
{
if (t == null)
return;
if (!t.hasLeft() && !t.hasRight()) // עלה
s.push(t.getValue());
else
{
leaves(t.getRight(), s); // קודם הצד הימני
leaves(t.getLeft(), s);
}
}
// ב.
public static boolean sameLeaves(BinNode<Integer> t1, BinNode<Integer> t2)
{
Stack<Integer> s1 = new Stack<Integer>();
Stack<Integer> s2 = new Stack<Integer>();
leaves(t1, s1);
leaves(t2, s2);
while (!s1.isEmpty() && !s2.isEmpty())
if (!s1.pop().equals(s2.pop())) // השוואת ערכי Integer
return false;
return s1.isEmpty() && s2.isEmpty(); // אותו מספר עלים
}סיבוכיות: O(n1 + n2) – כל צומת נסרק פעם אחת. (שתי המחסניות באותו סדר, ולכן אפשר להשוות אותן בהוצאה.)
לפניכם פעולה המקבלת מערך חד-ממדי ar מטיפוס שלם, ושני מספרים שלמים x ו-y הגדולים או שווים ל-0, קטנים מגודל המערך, ו-x ≤ y.
public static int go(int[] ar, int x, int y)
{
if (x == y) return (ar[x] % 10);
if ((ar[x] % 10) < (ar[y] % 10))
return go(ar, x + 1, y);
return go(ar, x, y - 1);
}| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| ar1 | 21 | 219 | 166 | 61 | 127 | 60 | 34 |
(1) מה יחזיר הזימון go(ar1, 2, 5)? הראו מעקב רקורסיבי עם הערכים x, y, ar1[x], ar1[y].
(2) מה יחזיר הזימון go(ar1, 0, ar1.length - 1)?
(3) כתבו מה מבצעת הפעולה go עבור מערך כלשהו ו-x, y כמתואר.
(1)
| x | y | ar1[x] | ar1[y] | ar1[x]%10 < ar1[y]%10 ? | פעולה |
|---|---|---|---|---|---|
| 2 | 5 | 166 | 60 | 6 < 0 – לא | go(2,4) |
| 2 | 4 | 166 | 127 | 6 < 7 – כן | go(3,4) |
| 3 | 4 | 61 | 127 | 1 < 7 – כן | go(4,4) |
| 4 | 4 | 127 | 127 | x == y | מחזירה 7 |
הזימון מחזיר 7.
(2) ספרות האחדות: 1, 9, 6, 1, 7, 0, 4 – יוחזר 9.
(3) הפעולה מחזירה את ספרת האחדות הגדולה ביותר מבין האיברים ar[x]..ar[y]. בכל צעד "נזרק" הקצה שספרת האחדות שלו קטנה יותר. סיבוכיות O(y − x).
נתון מערך arr, המכיל מספרים שלמים:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| arr | 3 | 4 | 9 | 6 | 8 | 4 | 2 |
נתון קטע תוכנית, הכתוב בשפת Java:
int sum = arr[0];
for (int i=1; i< arr.length; i++)
{
arr[i] = arr[i] - sum;
sum += arr[i];
}א. עקבו בעזרת טבלת מעקב אחר ביצוע קטע התוכנית, ורשמו את תוכן המערך arr לאחר ביצוע קטע התוכנית. בטבלת המעקב יש לכלול עמודה עבור כל אחד מן המשתנים האלה: i, arr[i], sum.
ב. כתבו דוגמה למערך arr בגודל 5 שלאחר ביצוע קטע התוכנית כל התאים שלו יכילו מספרים חיוביים (כלומר, מספרים הגדולים מאפס).
א. בהתחלה sum = arr[0] = 3. בכל סיבוב מחסירים מהתא את sum ואז מוסיפים לו את הערך החדש של התא.
| i | arr[i] (לפני) | arr[i] (אחרי) | sum |
|---|---|---|---|
| — | — | — | 3 |
| 1 | 4 | 1 | 4 |
| 2 | 9 | 5 | 9 |
| 3 | 6 | -3 | 6 |
| 4 | 8 | 2 | 8 |
| 5 | 4 | -4 | 4 |
| 6 | 2 | -2 | 2 |
תוכן המערך בסיום:
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| arr | 3 | 1 | 5 | -3 | 2 | -4 | -2 |
הסבר: אחרי כל סיבוב sum שווה לערך המקורי של arr[i] (כי sum + (arr[i] − sum) = arr[i]). לכן כל תא (חוץ מהראשון) הופך להפרש בין הערך המקורי שלו לערך המקורי של התא שלפניו.
ב. צריך תא ראשון חיובי ומערך עולה ממש, למשל:
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| arr | 1 | 2 | 3 | 4 | 5 |
לאחר הביצוע: 1, 1, 1, 1, 1 – כל התאים חיוביים (כל הפרש בין שני תאים סמוכים הוא 1).
במשחק קליעה למטרה, אדם יורה כמה חיצים למטרה. הניקוד עבור כל ירייה הוא מספר שלם מ־0 נקודות (החטאה מלאה) ועד 10 נקודות (פגיעה במרכז המטרה).
אדם מצליח במשחק אם מתקיים לפחות אחד מן המקרים האלה:
- בממוצע הניקוד הוא מעל ל־5.0 נקודות.
- ברוב היריות הוא זכה ב־6 נקודות ומעלה.
- בירייה אחת או יותר הוא זכה ב־10 נקודות.
דוגמה: אדם ירה חמישה חיצים וזכה בניקוד: 5, 7, 9, 3, 3. בממוצע הוא זכה ב־5.4 נקודות, ולכן הוא הצליח במשחק.
דוגמה נוספת: אדם ירה שישה חיצים וזכה בניקוד: 7, 6, 1, 7, 8, 0. בארבע יריות מתוך השש (כלומר, ברוב היריות שלו) הוא זכה ב־6 נקודות ומעלה, ולכן הוא הצליח במשחק.
דוגמה נוספת: אדם ירה שלושה חיצים וזכה בניקוד: 2, 10, 0. באחת מן היריות הוא זכה ב־10 נקודות ולכן הוא הצליח במשחק.
כתבו פעולה חיצונית ששמה success המקבלת מספר שלם – num. num מציין את מספר החיצים שהאדם ירה למטרה. הפעולה תקלוט את מספר הנקודות שזכה בהן האדם בעבור כל חץ שהוא ירה. הפעולה תחזיר true אם האדם הצליח במשחק, אחרת תחזיר הפעולה false.
רעיון: קולטים את כל num הניקודים ובמהלך הקליטה צוברים שלושה נתונים: סכום, מספר יריות של 6 ומעלה, ומספר יריות של 10. רק אחרי שכל הקלט נקלט בודקים את שלושת התנאים (מספיק שאחד מתקיים – ||).
public static boolean success(int num)
{
int sum = 0; // סכום הנקודות
int high = 0; // יריות של 6 ומעלה
int ten = 0; // יריות של 10
int score;
int i;
for (i = 0; i < num; i++)
{
score = scan.nextInt();
sum += score;
if (score >= 6)
high++;
if (score == 10)
ten++;
}
return (double) sum / num > 5.0 || high * 2 > num || ten > 0;
}הערות: הממוצע מחושב כממשי ((double) sum / num) כדי לא לאבד את החלק העשרוני (5.4). "ברוב היריות" = יותר מחצי: high * 2 > num.
כדי להצפין מסר כלשהו, מערבלים את סדר התווים שמרכיבים אותו (התווים עצמם נשמרים אך מיקומם משתנה). לדוגמה: לאחר ערבול סדר התווים של המילה World אפשר לקבל את המסר המוצפן rdoWl.
כדי שמקבל המסר המוצפן יוכל לפענח את המסר המקורי של השולח, מצורף למסר המוצפן "מערך מפענח". גודל המערך המפענח הוא כגודל המסר המוצפן, ובכל תא מופיע מספר האינדקס המקורי של כל תו.
לדוגמה: עבור המסר המוצפן rdoWl (של המסר המקורי World), המערך המפענח הוא:
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| arr | 2 | 4 | 1 | 0 | 3 |
הסבר: התו הראשון במסר המוצפן (r) מופיע במסר המקורי באינדקס 2, התו השני במסר המוצפן (d) מופיע במסר המקורי באינדקס 4, התו השלישי במסר המוצפן (o) מופיע במסר המקורי באינדקס 1, וכן הלאה.
א. רשמו את המערך המפענח עבור המסר Study, שהוצפן למסר dyutS.
ב. כתבו פעולה חיצונית ששמה originalText המקבלת מחרוזת מוצפנת – str ומערך מפענח – arr. הפעולה תחזיר את המחרוזת המקורית.
א. במסר המקורי: S=0, t=1, u=2, d=3, y=4. התווים המוצפנים לפי הסדר הם d, y, u, t, S, ולכן:
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| arr | 3 | 4 | 2 | 1 | 0 |
ב. רעיון: בונים את המחרוזת המקורית לפי הסדר – מקום 0, מקום 1, ... . עבור כל מקום k מחפשים במערך המפענח את התא j שבו arr[j] == k, ומוסיפים את התו str.charAt(j).
public static String originalText(String str, int[] arr)
{
String res = "";
int k;
int j;
for (k = 0; k < arr.length; k++) // מקום k במסר המקורי
{
for (j = 0; j < arr.length; j++) // מחפשים את התו שמקומו המקורי k
{
if (arr[j] == k)
res = res + str.charAt(j);
}
}
return res;
}לדוגמה: originalText("rdoWl", {2,4,1,0,3}) מחזירה "World". סיבוכיות: O(n²).
נתונה המחלקה Results, המייצגת זמני שחיית 50 מטרים של שחיין בתקופה מסוימת. למחלקה שתי תכונות:
- arr – מערך מטיפוס שלם, המכיל את מספר השניות שארכה שחיית 50 המטרים של השחיין בכל יום בתקופה מסוימת
- name – שם השחיין, מטיפוס מחרוזת
הניחו שקיימות פעולות get/set עבור תכונות המחלקה.
זמן שחיית 50 המטרים של השחיין יכול להשתנות מיום ליום. "הרעה" היא יום שבו ארכה השחייה יותר שניות מביום הקודם, ו"שיפור" הוא יום שבו ארכה השחייה פחות שניות מביום הקודם. "הפרש" הוא ההבדל במספר השניות בערך מוחלט בין יום מסוים ליום שקדם לו.
א. כתבו פעולה פנימית ששמה deltas המקבלת פרמטר – isLonger מטיפוס בוליאני, ומחזירה מספר שלם בהתאם לאפיון שלפניכם:
- אם הפרמטר isLonger שהתקבל הוא true, הפעולה תחזיר את סך ההפרשים בימים שנחשבים "הרעה".
- אם הפרמטר isLonger שהתקבל הוא false, הפעולה תחזיר את סך ההפרשים בימים שנחשבים "שיפור".
לדוגמה, עבור המערך arr שלפניכם, אם isLonger הוא true הפעולה תחזיר 14, ואם isLonger הוא false הפעולה תחזיר 17.
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | |
|---|---|---|---|---|---|---|---|
| arr | 45 | 52 | 50 | 53 | 38 | 38 | 42 |
הסבר: התאים באינדקסים 1, 3, 6 מייצגים ימים שנחשבים "הרעה", וסך ההפרשים בהם (7 + 3 + 4) הוא 14 שניות. התאים באינדקסים 2, 4 מייצגים ימים שנחשבים "שיפור", וסך ההפרשים בהם (2 + 15) הוא 17 שניות.
ב. כתבו פעולה פנימית ששמה improving המחזירה true אם סך ההפרשים בימים שנחשבים "שיפור" גדול מסך ההפרשים בימים שנחשבים "הרעה". אחרת הפעולה תחזיר false. למשל, בדוגמה שלעיל הפעולה תחזיר true (17 > 14). חובה להיעזר בפעולה שכתבתם בסעיף א.
א. עוברים על הימים מהיום השני ומחשבים diff = arr[i] - arr[i-1]. הפרש חיובי = הרעה, הפרש שלילי = שיפור (ואז מחברים את הערך המוחלט, -diff). הפרש 0 אינו נספר באף אחד.
ב. משתמשים ב־deltas פעמיים ומשווים.
public class Results
{
private int[] arr;
private String name;
// פעולות get/set קיימות
// א
public int deltas(boolean isLonger)
{
int sum = 0;
int diff;
int i;
for (i = 1; i < arr.length; i++)
{
diff = arr[i] - arr[i - 1]; // חיובי = הרעה, שלילי = שיפור
if (isLonger && diff > 0)
sum = sum + diff;
else if (!isLonger && diff < 0)
sum = sum - diff; // ערך מוחלט
}
return sum;
}
// ב
public boolean improving()
{
return deltas(false) > deltas(true);
}
}