שאלות מבחן
הנחות
- ניתן להשתמש בטיפוס
boolובפונקציהmallocללא שימוש ב-include#. - ניתן להניח שסיבוכיות הזמן של
malloc(n)היא $\Theta(1)$ לכלn. - שלום שלום, ( \Theta = 5 ).
- למשל אפשר לקבל \( x = 5 \)
- ואולי גם אפשר לכתוב \( \theta(5) = 5 \)
שאלה 1
נתון מערך שלמים שאורכו n, בו כל האיברים אי-שליליים (גדולים/שווים ל-0) וקטנים ממש מקבוע x נתון. נגדיר מערך k-יחודי בתור מערך בו אם האיבר a מופיע, כל המספרים שבמרחק k ממנו לא מופיעים. כלומר, בהנתן איבר a במערך k-יחודי, האיברים הבאים לא מופיעים:
$ a-k, a-(k + 1), …, a, a+1, …, a +k $
למשל, המערך הבא הוא 2-יחודי, ואינו 3-יחודי:
$$ [1, 4, 10, 13] $$
סעיף א
כתבו פונקציה המקבלת מערך ואורכו, וקובעת האם הוא 1-יחודי. על הפתרון לעמוד בדרישות סיבוכיות זמן \( \Theta(n + x) \) וסיבוכיות מקום \( \Theta(x) \).
פתרון
ננסה לפרק את הדרישה. מערך 1-יחודי הוא מערך בו אם המספר a מופיע, המספרים a - 1 ו-a + 1 אינם מופיעים. השאלה מאפשרת להשתמש בסיבוכיות מקום $\Theta(x)$ - זו אינדיקציה טובה מאוד להשתמש במערך באורך $x$. אפשר להשתמש במערך כזה על מנת לסמן את האיברים שכבר פגשנו בדגל בוליאני - בהגעה לאיבר a, נבדוק האם פגשנו כבר את האיברים a - 1 ו-a + 1. כמו כן, נבדוק האם a לא מופיע פעמיים.
נבדוק בזהירות את האינדקסים על מנת לוודא שאנחנו לא חורגים מגודל המערך ונקבל את הפתרון הבא:
bool is_one_unique(int* arr, int n, int x) {
bool* visited = malloc(sizeof(bool) * x);
for(int i = 0; i < x; i++) {
visited[i] = false;
}
bool is_unique = true;
for(int i = 0; i < n; i++) {
int a = arr[i];
if((a >= 0 && visited[a - 1]) ||
(a < x - 1 && visited[a + 1]) ||
visited[a]) {
is_unique = false;
break;
}
visited[a] = true;
}
free(visited);
return true;
}
מבחינת סיבוכיות, הפונקציה שלנו מבצעת שתי לולאות של x ו-n איטרציות בהתאמה, ובכל אחת מבצעת מספר קבוע של פעולות, ולכן סיבוכיות הזמן היא אכן $\Theta({n+x})$. סיבוכיות המקום היא $\Theta(x)$.
סעיף ב
כתבו פונקציה שמקבלת את אותם הפרמטרים ומחזירה האם מערך הוא k-יחודי. על הפתרון לעמוד בסיבוכיות זמן של $\Theta({nk + x})$.
פתרון
הגישה לא מאוד שונה מהסעיף הקודם. טריק חשוב שניתן ללמוד בשאלה הוא להשתמש בסיבוכיות הזמן המבוקשת בשביל להבין מה הפתרון הנדרש. בשביל לקבוע האם מערך הוא k-יחודי כש-$1 = k$, קיבלו סיבוכיות של $\Theta({n+x})$, השקולה ל-$\Theta({nk+x})$ עבור $1=k$. לכן נדרש רק להתאים מעט את הגישה שלנו.
לכל מספר a במערך, במקום לבדוק אותו רק מול שני שכניו המיידים, כעת יש לבדוק אותו מול k שכניו - זה יהפוך ל-$nk$ בשתי לולאות מקוננות:
bool is_one_unique(int* arr, int n, int x) {
bool* visited = malloc(sizeof(bool) * x);
for(int i = 0; i < x; i++) {
visited[i] = false;
}
bool is_unique = true;
for(int i = 0; i < n; i++) {
int a = arr[i];
int start = (a - k < 0) 0 : a - k;
int end = (a + k >= x) ? x - 1 : a + k;
for(int j = start; j <= end; j++) {
if(visited[j]) {
is_unique = false;
break;
}
}
visited[a] = true;
}
free(visited);
return true;
}
שאלה 2
נתוך מערך שלמים באורך n. לכל אינדקס i במערך, נגדיר פונקציה $f(i)$ שערכה הוא ההפרש בין סכום האיברים בין 0 ל-i (כולל) לבין סכום האיברים בין i + 1 ל-n - 1:
$ f(i) = \sum_{j = 0}^{i}arr[i] - \sum_{j=i+1}^{n - 1}arr[i] $
למשל, עבור המערך [1, 5, 3-, 10, 2] והאינדקס i = 1, מתקיים:
$ f(i) = (2 + 10) - (-3 + 5 + 1) = 12 - 3 = 9 $
כתבו פונקציה המקבלת מערך ואורכו ומחזירה את האינדקס עבורו $f(i)$ מינימלית. אם יש מספר אינדקסים $i,j$ עבורם $f(i) = f(j)$ ניתן להחזיר כל אחד מהם. על הפתרון שלכם לעמוד בסיבוכיות זמן $\Theta(n)$ וסיבוכיות מקום $\Theta(1)$.
פתרון
זו שאלה שדורשת מעט להתחכם עם הגישה שלנו. פתרון אחד יכול להיות לחשב מפורשות את $f(i)$ לכל $i$ בגבולות המערך ולקחת את המינימלי - עם זאת, זה לא עומד בדרישות הסיבוכיות (חישוב n פעמים סכום של כל המערך יגיע לסיבוכיות $\Theta(n^2)$).
עם זאת, נשים לב שאם נעשה חישוב עזר קטן של סכום איברי המערך, נוכל לחסוך חישוב חוזר בכל איטרציה. נסמן:
- $s$ יהיה סכום כל המערך (אותו ניתן לחשב בריצה אחת על איברי המערך ב-$\Theta(n)$)
- $_1p$ יהיה סכום איברי המערך מ-
0ועדiכלשהו (האיבר הראשון ב-$f(i)$) - $_2p$ יהיה סכום איברי המערך מ-
i + 1ועדn - 1(האיבר השני ב-$f(i)$)
בהנחה שחישבנו את $s$ ו-$_1p$, מתקיים:
$ p_1 + p_2 = s \Longrightarrow p_2 = s - p_1 $
כלומר, אפשר לחשב את סכום המערך, ואז לבצע איטרציה על המערך, לעדכן את שני הסכומים ולבחור את המינימלי שראינו עד כה.
int min_f(int* arr, int n) {
int sum = 0;
for(int i = 0; i < n; i++) {
sum += arr[i];
}
int s1 = 0, s2;
int min_val = INT_MAX;
int min_index = 0;
for(int i = 0; i < n; i++) {
s1 += arr[i];
s2 = sum - s1;
if(s1 - s2 <= min_val) {
min_val = s1 - s2;
min_index = i;
}
}
return min_index;
}
שאלה 3
בהנתן מערך ממוין arr, נגדיר נקודת שבת בתור אינדקס i עבורו מתקיים arr[i] = i. כתבו פונקציה שמקבלת מערך ואורכו ומחזירה אינדקס של נקודת שבת אם קיימת, אחרת 1-. על הפתרון לעמוד בסיבוכיות זמן של $\Theta(\log n)$ וסיבוכיות מקום $\Theta(1)$.
פתרון
שאלה 4
נתון מערך מספרי int באורך n.