תאריך הבחינה:... נובה פנדינה שם המרצה: רפי כהן שם המתרגל: יסודות מבני נתונים שם הקורס:..00 מספר הקורס:. סמסטר: א' מועד: שנה: שלוש שעות משך הבחינה: ללא חומר עזר חומר עזר: ב' הנחיות חשובות: רצוי לפתור את המבחן תחילה במחברת הטיוטה. לאחר מכן להעתיק את התשובות למקום המיועד בטופס התשובות. בדיקת המבחן לא תביא בחשבון את מחברת הטיוטה. מחברות הטיוטה יילקחו לגריסה עם תום הבחינה. המבחן מורכב מ שאלות. יש לענות על כל השאלות. בסעיפים בהם קיימת דרישת זמן לאלגוריתם שעליכם להציע, יש לתת הסבר מספק שאכן עמדתם בדרישת הזמן שבשאלה. ניתן להשתמש במבני נתונים ואלגוריתמים שנלמדו בכיתה ללא פירוט של המימוש שלהם אלא אם השאלה דורשת במפורש פירוט של המימוש. במידה וקיים יותר ממימוש אחד ניתן לבחור כל מימוש שמתאים לזמני הריצה המבוקשים ולתנאי השאלה. במידה ואינכם יודעים את התשובה לסעיף כלשהו, רשמו "לא יודע/ת" )במקום תשובה( ותזכו ב- 20% מניקוד הסעיף. לא ניתן לכתוב לא יודע על חלק מסעיף. שאלה 2 3 4 ציון ציון
שאלה )20 נקודות(: סעיף א )0 נקודות( נתונים קטעי קוד. לכל אחד מהקטעי קוד קבעו מהו זמן ריצתם. רשמו את הנוסחה המתארת זמן ריצה של קטע קוד ומצאו חסם אסימפטוטי הדוק ביותר. נמקו תשובתכם!. Magic(n){ If (n==0) return ; x 0; for ( i to n) x x+magic(n-) return x; } T(n)= nt(n-)+; T(0)=; נפתח באיטרציות את נוסחת הנסיגה ונקבל: T(n)=Θ(n!) 2. Function(n){ x 0; for (i n; i>0; i i/2) x Magic(i); return x; } T(n)=n!+(n/2)!+(n/4)!+ +(n/2^(log n))! נחסום מלמלה על ומלמטה ונקבל : kn! T(n) c logn n! * יש פתרון מדויק יותר, את הפתרון הנ"ל קיבלנו כנכון. סעיף ב ) נקודות(:
נתון מערך בעל n מספרים שלמים. ההפרש המקסימאלי השמאלי מוגדר כהפרש הגדול ביותר בין איבר מערך כלשהו וכל האיברים המופיעים משמאלו במערך. לדוגמה, במערך: 2 3 8 9 4 ההפרש המקסימאלי השמאלי הינו 6, כיוון שההפרש בין 0 ולבין. )הנמצא משמאלו( הינו הכי גדול. MaxDiffSlow(A){ max=a[]-a[0]; for(j 2 to A.length) for(i 0 to j) if(a[j]-a[i]>max) max a[j]-a[i]; return max;. ). נקודות( נתון פסאודו קוד. האם האלגוריתם פותר את בעיית מציאת ההפרש המקסימאלי השמאלי? מהו זמן הריצה של האלגוריתם?. אלגוריתם עובר על כל הזוגות הרלוונטיים ומחשב את ההפרש השמאלי. מכל התוצאות אלגוריתם בוחר בהפרש המקסימאלי. כלומר, אלגוריתם פותר את בעיית ההפרש המקסימאלי השמאלי. זמן ריצתו: O(n^2) במקרה הגרוע. )7 נקודות( כתוב פסאודו-קוד של אלגוריתם המוצא הפרש מקסימאלי שמאלי במערך הנתון בזמן ריצה ליניארי. ניתן להשתמש ב ()O זכרון נוסף. הרעיון הוא להתחיל לעבור על המערך משמאל, ובכל שלב לזכור )במשתנה )min את הערך המינימאלי שראינו עד כה, ואת ההפרש השמאלי הגדול ביותר עד כה )נשמור במשתנה.)diff צריך לשים לב שעבור מספר מסויים במערך הנמצא במקום i ההפרש השמאלי הגדול עבורו נקבע על פי האיבר עצמו והאיבר הכי קטן שראינו עד עכשיו- כלומר, אין צורך לבצע חישוב עבור כל האיברים מהראשון ועד ה i, אלא עבור איבר הקטן ביותר שראינו עד כה. MaxDiffFast(A){ min=a[0]; diff=a[]-a[0]; for(i= to n-){ if(a[i]-min > diff) diff=a[i]-min; if(a[i]<min) min=a[i]; }// for }//algorithm
שאלה )2 2 נקודות(. על לוח משחק בגודל m m מונחים k כלי משחק זהים. בהינתן m ומיקומי הכלים )כאשר מיקומו של כלי נקבע עפ"י מספר השורה ומספר העמודה המתאימים זוג אינדקסים,)(i,j) עליך להיערך בזמן O(k), כאשר ברשותך זיכרון נוסף בגודל בלבד, לתמיכה בפעולות הבאות בזמן צפוי ()O : O(k),x, p ( מצא את מספר הכלים שנמצאים במרחק y) בהינתן נקודה Neighbors (p) של צעד אחד מ-, p כאשר בצעד אחד מותר לנוע אל אחת מבין לכל היותר 8 המשבצות הסמוכות. ניתן להניח כי p הינו מיקום חוקי בתחום הלוח. דוגמא: p במקרה זה התשובה היא. 4 p 2. p 2 אם קיים כבר כלי ב- p ל- Move(p,p2) הזז את כלי המשחק הנמצא ב- p 2 שני מיקומים חוקיים p ו עליך לדווח שגיאה, ולא לבצע את ההזזה. ניתן להניח כי בתחום הלוח. כתבו את עיבוד הקדם ואת מימוש השאילתות. נתחו זמני ריצה. נשתמש בטבלת גיבוב עם chaining בגודל k. בתרגולים ראינו פונקציות גיבוב של משתנה יחיד, ניתן להרחיבם בקלות לפונציות גיבוב של 2 משתנים ומעלה. אפשרות נוספת היא להמיר זוג אינדקסים למספר בודד באופן הבא: נמיר את זוג האינדקסים (i,j) למספר יחיד בתחום :[..m*m] T(i,j) = (i-)*m+j ההמרה היא חח"ע, ולכן גם הפיכה. עיבוד מקדים: נכניס את כל מיקומי הכלים לטבלת הגיבוב. מכיוון שמספר הכלים זהה למספר האיברים בטבלה, מקדם העומס הוא, ולכן כל ההכנסות )והמחיקות( יתבצעו בזמן ()O. הזמן הכולל של העיבוד המקדים הוא איתחול טבלה בגודל k, ועוד k הכנסות, ולכן הוא שווה ל O(k) בממוצע. 4 :Neighbors (p) נחשב ל,(x,y) את 8 האינדקסים השכנים ) ( כאשר ) (, לכל אינדקס שכן שמצאנו, נוודא שהאינדק חוקי, כלומר נמצא בתחום לוח המשחק, לדוגמא (0,) איננו אינדקס חוקי. אם האינדקס השכן חוקי נבדוק האם הוא קיים בטבלת הגיבוב, ונסכום את מספר האיברים שמצאנו.
חישוב אינדקסי השכנים, ובדיקה שהם חוקיים, מתבצעת בזמן קבוע. חיפוש בטבלת הגיבוב, תתבצע לכל היותר 8 פעמים, ולכן עלות הפעולה בסך הכל היא ()O בממוצע. Move(p,p2) נבדוק תחילה האם האינדקס p2, נמצא בטבלת הגיבוב, אם כן, נחזיר שגיאה. אחרת, נמחק את האינדקס p, מטבלת הגיבוב, ונכניס את האינדקס p2 לטבלה. אנו מבצעים לכל היותר 3 פעולות, ולכן זמן הריצה הוא ()O בממוצע. שאלה ) 3 נקודות( מערך A עם n איברים נקרא כמעט ממויין )k<n( k אם A[i],A[j] לכל i,j המקיימים.j-i>k במילים אחרות, המערך לא חייב להיות ממויין, אבל כל שני איברים הנמצאים בסדר הפוך, לא יכולים להיות רחוקים זה מזה יותר מ- k מקומות. סעיף א )8 נקודות(: 8 תן דוגמה למערך כמעט ממויין 3 תאים, אשר בעל מערך כמעט איננו ) נקודות( ממויין.. )2 נקודות( מערך כמעט ממויין הינו מערך ממוין. נכון/לא נכון )סמן התשובה הנכונה(... for(i=0;i<n; i = i+k){ 2. sort (A,i,i+k-) 3. } ) נקודות( בהינתן האלגוריתם הבא:.. אשר ממיין k -איברים עוקבים ב- A בצורה סדרתית. לדוגמא: עבור המערך מסעיף א', האלגוריתם ימיין את האיברים 0,,2, ולאחר מכן את האיברים 4,,. וכן הלאה. הוכח, אם לא ספק דוגמא האם האלגוריתם הופך מערך כמעט ממויין k נגדית. לממויין? אם כן sort האלגוריתם איננו הופך מערך כמעט ממויין k לממויין. 4. ) נקודות( מהו זמן הריצה של האלגוריתם, בהנחה שהפונקציה איברים בזמן?O(mlogm) פועלת על m
הפונקציה sort פועלת על n/k בלוקים )בקירוב( k הריצה הוא ) ( ) ( בגודל לכל היותר, ולכן זמן סעיף ב ) נקודות(: הצע אלגוריתם המבוסס על מיון מהיר אשר מקבל מערך והופך אותו לכמעט ממויין k. האלגוריתם החדש צריך להיות יעיל יותר מהאלגוריתם המקורי. מהו זמן הריצה האסימפטוטי של האלגוריתם החדש במקרה הטוב ביותר? רמז: אילו שורות צריך לשנות במיון מהיר על מנת שיבצע את הנדרש? צריך להחליף את שורה באלגוריתם המקורי )מופיע למטה( if (r-p>k) בשורה הבאה: נוסחת הרקוסיה של האלגוריתם החדש נשארת אותו דבר, אולם בסיס הרקוריסה משתנה: ( ) ( ) ( ) ( ) אם נפתח את הנוסחה כעץ רקורסיה נקבל כי גובה העץ הינו מבוצעות O(n) פעולות, ולכן זמן הריצה הינו., ובכל רמה שאלה )2 4 נקודות( גרפים,V G ( גרף לא מכוון הנתון ע"י רשימות הסמיכויות lists).(adjacency עבור יהי (E קבוצת קודקודים S V יהי OUT(S) מספר הקשתות e E כך שבדיוק אחד משני קודקודי e שייך ל-. S 2 6 לדוגמא:
3 4 עבור הגרף הנתון באיור ו- {2,4,}=S,.OUT(S) = 3 הצע אלגוריתם, אשר מקבל גרף וקבוצת קודקודים S )הניתנת כרשימה משורשרת( ומדפיס את.OUT(S) אין הגבלת זיכרון נוסף. זמן הריצה של האלגוריתם: log S V S בבדיקה קיבלנו כל אלגוריתם המבצע את הנדרש ללא תלות בזמן ריצתו. )בהנחה וזמן ריצה של אלגוריתם מנותח בצורה נכונה(. אחד הפתרונות: לבנות מקודקודי קבוצה S עץ AVL על פי המפתחות שלהם. בזמן: S log S לאחר מכן, עבור כל קודקוד של קבוצה S לעבור על רשימת הסמכיות שלו : לכל שכן לחפש אותו בעץ.AVL עבור כל שכן שלא נמצא בעץ AVL נספרו לתשובה סופית. זמן ריצה: לכל קודקוד יכולים להיות מקסימום Vשכנים, לחפש כל אחת בעץ AVL לוקח (S O(log זמן. ס"ה עבור השלב השני: S. V log S ס"ה עבור כל האלגוריתם: S. V Log S שאלה )24 נקודות( בשאלה זו אתם נדרשים לממש ערימת מקסימום בעזרת עץ בינארי )במקום המימוש הרגיל בעזרת מערך(. הציעו מבנה נתונים עבור קבוצת איברים בעלי מפתחות מספרים שלמים. על מבנה הנתונים להעזר במבנה נתונים עץ בינארי על מנת לתמוך בפעולות הבאות: פעולה תיאור זמן )במקרה הגרוע( אתחול מבנה נתונים. בהתחלה המבנה ריק )לא ) ( מכיל איברים(. tini () (log (n. k הכנסת איבר חדש בעל מפתח MaxHeapInsert (k) Max() החזרת ערך מקסימאלי הנמצא במבנה ) ( 7
(log n) (log n) הוצאת איבר בעל ערך מקסימאלי הנמצא במבנה הפונקציה מקבלת מצביעים לשורשים של שני עצים מושלמים המהווים ערימות מקסימום. פונקציה זו צריכה למזג שתי ערימות אלו לתוך ערימת מקסימום אחת, ולהחזיר מצביע לשורש. ExtractMax(k) Merge( ) אתם רשאים להשתמש רק במבנה נתונים שהוא עץ בינארי )עם מצביעים לילדים ואבות בכל קודקוד(. ניתן להישתמש בזיכרון נוסף בגודל קבוע. פרטו לגבי מבנה הנתונים שאתם מציעים )אובייקטים, שדות וכו'(, וכתבו מקוצר הסברים עבור מימוש של הפעולות. הרעיון בשאלה זו להשתמש בעץ בינארי )רגיל(, כאשר תוך כדי הכנסה\מחיקה נשמור על הצורה הכמעט מאוזנת של העץ וגם על תכונת המפתחות של ערימת מקסימום. נחזיק בכל שלב מצביע next אשר יצביע למרום פנוי הבאה בעץ עליו אפשר להכניס איבר חדש. )המקום האפשרי הבא הינו מקום אשר לא פוגע בצורה הכמעט המאוזנת של העץ(. כלומר, נמלא את העץ לפי רמות, משמאל לימין, באופן סדרתי. אחרי כל פעם שנכניס קודוקד, עלינו למצוא מקום פנוי הבא האפשרי. next =null נאתחל עץ ריק. Init() (k) -MaxHeapInsert נצרך קודקוד חדש כעלה למקום עליו מצביע.next עתה ננמצא מהו מקום אפשרי פנוי הבא בעץ. הכלל הוא כזה: אם הקודקוד החדש הצטרף כבן שמאלי לאביו, אזי המקום הפנוי הבא הינו הבן הימני של אביו. אחרת )אם הקודקוד החדש הצטרף כבן ימני לאביו(: נעלה בעץ למלה בחיפוש לאב קודמון ראשון כך שהקודקוד החדש שנכנס הינו צאצא השמאלי שלו. מקודקוד זה נלך לילד ימני שלו, ומשם נתקדם הכי השמאלי שניתן. אופן הכנסה זה שומר על העץ להיות כמעט שלם בכל שלב, ולכן הגובה שלו יהיה.O(log n) לאחר הכנסת הקודוקד כעלה, נפעפע את הערך שלו למעלה על פני העץ, עד המקום הנכון שלו )במידה וצריך(, בדומה לפונקצית UpHeapify שראיתם בתרגול. זמן ריצה: (n O(log -ExtractMax() נשים את המפתח של האיבר האחרון שנכנס למבנה במקום הערך של השורש. נמחוק את האיבר הזה פיזית מהעץ. לאחר מכן נתקן את הערימה עד לחוקית )בדומה לMaxHeapify ( ) Merge( נמחוק מעץ H2 את הקודקוד שנכנס אחרון. הוא יהיה השורש החדש. העץ H יהיה בן השמאלי העץ H2 )לאחר המחיקה( יהיה עץ ימני. עתהבן 8
שמאלי ערימה חוקית, בן ימני- ערימה חוקית, ביחד עם השורש לאו דווקא ערימה חוקית. נתקנה בדומה ל.MaxHeapify בהצלחה! דף עזר יסודות מבני נתונים 202-- 0