1 / 30

מערכים, מטריצות דלילות, ורשימות מקושרות

Lecture2 of Geiger & Itai’s slide brochure www.cs.technion.ac.il/~dang/courseDS. מערכים, מטריצות דלילות, ורשימות מקושרות. חומר קריאה לשיעור זה. Chapter 11.2– Linked lists (204 – 213).  Geiger & Itai, 2001. מערך כטיפוס נתונים אבסטרקטי. מערך מוגדר ע"י הפעולות הבאות :

emil
Download Presentation

מערכים, מטריצות דלילות, ורשימות מקושרות

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Lecture2 of Geiger & Itai’s slide brochure www.cs.technion.ac.il/~dang/courseDS מערכים, מטריצות דלילות, ורשימות מקושרות חומר קריאה לשיעור זה Chapter 11.2– Linked lists (204 – 213) Geiger & Itai, 2001

  2. מערך כטיפוס נתונים אבסטרקטי מערך מוגדר ע"י הפעולות הבאות: create(type,I)– מחזיר מערך A של איברים מטיפוס type כאשר האינדקסים הם הקבוצה הסופית I. get(A,i) – מחזיר את הערך של האיבר עם אינדקס i  I בתוך A. store(A,i,e) – מאחסן במערך A, תחת אינדקס i  I, את ערך הביטוי e. I בשפת c האינדקסים הם הקבוצה I={0,…,n-1} אחזר A[i] (get) שמור A[i] = e (store) • כללים הנשמרים ע"י הפעולות: • כל הערכים במערך מאותו טיפוס, והוא נקבע בהכרזה (type). • הערך המאוחזר לפי אינדקס i , הוא הערך האחרון שנשמר לפי אינדקס i. • אחזור לפי אינדקס i מחזיר ערך בלתי מוגדר אם מעולם לא אוחסן ערך לפי אינדקס זה. cs,Technion

  3. 10240 base 10241 10242 0 10243 offset 1 10244 2 10245 3 10246 הפעולות:מתבססות על חישובי כתובות Base+offset 4 10247 5 10248 6 10249 7 10250 8 10251 9 10252 10 10253 11 10254 12 10255 13 10256 14 10257 15 10258 16 10259 17 10260 10261 10262 מימושים למערך (1) address אזור זיכרון רצוף: כל הפעולות מתבצעות בזמן O(1). cs,Technion

  4. n base דוגמא: n=6 הכתובת של A[16] היא: base[16/6 ] + 16%6 = base[2] + 4 n k blocks n זמן חישוב הכתובת:O(1). מימושים למערך (2) מימוש באמצעות מספר אזורים בזיכרון: לכל אזור גודל קבוע n. כדי לממש מערך בגודל m יש לבחור k=m/n. נקצה מערך עזר (base) בגודל k האיבר A[i] נמצא בכתובת base[i/n ]+ i%n. cs,Technion

  5. מערך דו-ממדי int a[m][n] כל שורה A[i] היא מערך חד-ממדי באורך n. v v v נניח ש-base היא הכתובת של A[0][0] והמימוש הוא באזור זיכרון רצוף. הכתובת של שורה A[i] היא base + in הכתובת של איבר A[i][j] היא base + in + j cs,Technion

  6. i3 2 n2 1 i2 0 i1 n1 מערך רב-ממדי (1) במערך דו-ממדי:int A[n2][n1] הכתובת של איברA[i2][i1]מחושבת, כפי שראינו, ע"י הנוסחה:base + i2n1 + i1 במערך תלת-ממדי:int A[n3][n2][n1] הכתובת של איברA[i3][i2][i1]מחושבת ע"י הנוסחה:base + i3n2 n1 + i2n1 + i1 i3n2 n1 + i2n1 + i1 גודל מישור אורך שורה הערה: החישוב מניח קונוונצית ציינים ב-C (a[0] הוא המקום הראשון)

  7. אבל אפשר לבטא את הכתובת באופן אלטרנטיבי: = base + ((… ((idnd-1+id-1)nd-2 + id-2)nd-3 + … + i3 )n2+i2)n1 + i1 כמה פעולות במקרה הגרוע? מערך רב-ממדי (2) במערך d-ממדי:int A[nd]…[n3][n2][n1] הכתובת שלA[id]…[i3][i2][i1]מחושבת ע"י הנוסחה: base + idnd-1 …n1 + id-1 nd-2 …n1 +…+ i1 כמה פעולות במקרה הגרוע?

  8. מערך רב-ממדי (3) הנוסחה לחישוב הכתובת במערך רב ממדי דומה לכלל הורנר לחישוב ערך פולינום: p(x0) = akxk + ak-1xk-1 + …+ a1x + a0 = (…(akx + ak-1)x +…+ a1)x+a0 התוכנית הבאה מחשבת את ערך הפולינום בנקודהx: p=a[k]; for (j=k-1; j>=0; j--) p = px + a[j] באופן אנלוגי, התוכנית הבאה מחשבת את הכתובת שלA[id]…[i3][i2][i1]הנתונה ע"י:base + ((… ((idnd-1+id-1)nd-2 + id-2)nd-3 + … + i3 )n2+i2)n1 + i1 addr=i[d]; for (j=d-1; j>=1; j--) addr = addr*n[j] + i[j]; addr= base + addr;

  9. n1 n2 סריקת מערך רב-ממדי זמן סריקת כל איברי מערך A[nd]…[n3][n2][n1] תלוי בסדר הסריקה. סריקה לפי סדר האחסון מהירה יותר בגלל לוקליות של גישה לזיכרון. במימוש שהצגנו, סריקה כאשר האינדקסים הנמוכים נסרקים ראשונים מהירה יותר מסריקה בה האינדקסים הגבוהים נסרקים ראשונים. השיפורהוא בקבוע בלבד ואינו מתבטא בסימון O (בהנחה ש-ni הם קבועים)

  10. איתחולמערך שאלה: כמה עולה לאתחל מערך באורך n? תשובה: איתחול נאיבי – O(n) נתאר מבנה נתונים לאיתחול מערך בזמן קבוע! הרעיון: לא באמת נאתחל את המערך. נשתמש בזיכרון נוסף לציין האם כבר נכתב ערך בתא במערך cs,Technion

  11. נסיון לפתרון נשתמש במשתנה עזר top:יציין כמה איברים במערך "נכתבו" נשתמש גם במערך עזר C: לכל i < topC[i] , מאחסן את האינדקס (ב-A) של האיבר ה-(i+1) שאותו כבר "כתבנו" המציינים ערכיםהמוגדרים • 453 • 5 זבל • 643 • 4 top • 123 • 3 • 0 • 2 • 1 • 1 מציינים מוגדרים • 4 • 0 A C

  12. top top המציינים מצביעים V ערכים לאזור הבטוח המוגדרים • 333 • 234 • 453 • 5 • 5 • 5 זבל • 7 • 0 • 643 • 4 • 4 • 4 • 222 • 675 • 123 • 3 • 3 • 3 • 453 • 111 • 554 • 101 • 2 • 2 • 2 • 643 • 453 top • 131 • 224 • 177 • 1 • 1 • 1 • 123 • 643 מציינים מוגדרים • 212 • 131 • 4 • 0 • 0 • 0 • 0 • 123 • 1 A B C • 101 • 4 • 1 בדוגמא זו: V[0]=5, V[1]=3, V[4]=7 • 4 איתחולמערך בזמן O(1) דוגמא: לא O(1) ! כמה "יעלה" זמן גישה? cs,Technion

  13. המציינים מצביעים V ערכים לאזור הבטוח המוגדרים • 234 • 453 • 5 • 5 זבל • 0 • 643 • 4 • 4 top • 675 • 123 • 3 • 3 • 554 • 0 • 2 • 2 • 224 • 1 • 1 • 1 מציינים מוגדרים • 131 • 4 • 0 • 0 A B C הקוד לאיתור זבל int is_garbage(int i) { return !(( B[i] < top && (B[i] >= 0) && C[B[i]] = = i)); } דוגמא לאיבר שהוכנס למבנה: C[B[4]] = = 4 דוגמאות לזבל: C[B[5]] > top וכן C[B[3]]= = 0

  14. הקוד לפעולותהמערך top = 0; constant = const; אתחל init(V,const): if (is_garbage(i)) return constant; else return A[i]; אחזר get(V,i): if (is_garbage(i)){ C[top] = i ; B[i] = top ; top = top +1;} A[i]= e; שמור store(V,i,e): הערה: ברגע שהמערך מתמלא(top > n) ניתן להשתמש במערך A בלבד כיוון שאין, ויותר לא יהיה, זבל ב-A.

  15. ייצוג מטריצות סימטריות מטריצה סימטרית היא מערך A דו-ממדי המקיים A[i,j]=A[j,i]. הייצוג: אוסף וקטורים המאוחסנים במערך רציף יחיד: 12 34 5 67 8 9 10 מהי הכתובת של איבר A[i,j] ? נשתמש בנוסחה: 1+2+…+i = i(i+1)/2 { חצי תחתון Base + i ( i+1 )/2 + j if i  j Addr(j,i) Otherwise Addr(i,j)= חצי עליון

  16. ייצוג מטריצות דלילות מטריצה דלילה היא מערך דו-ממדי עבורו "רוב מוחלט" של האיברים שווה לאפס (או לקבועc כלשהו).מהו רוב מוחלט ? בדוגמא זו 7 מתוך 28 איברים שונים מהקבוע. הגדרה אסימפטוטית: אם סדרת מטריצות M1...Mn ...מקיימת שמספר האיברים השונים מקבוע c הוא מסדר גודל של o(m(n)) (o קטן) כאשר m(n) הוא מספר המקומות המקסימלי במטריצה Mn אז זוהי סדרה של מטריצות דלילות. דוגמא: מטריצות אלכסוניות. במטריצות ריבועיות דלילות מגודל nn מספר האיברים השונים מקבוע c הוא o(n2). ייצוג: "רשימת" שלשות(i,j,Aij) מסודרות בסדר לקסיקוגרפי. דוגמא: (0,2,3), (0,5,1), (1,1,5), (2,3,4), (4,1,2), (4,4,7), (4,5,8)

  17. חסרונות ויתרונות של הייצוג הייצוג: רשימת שלשות(i,j,Aij) מסודרות בסדר לקסיקוגרפי. חסרון עקרי: אין גישה אקראית לפי מציין בזמן O(1). יתרונות: חוסך בזיכרון עבור מטריצות דלילות. מאיץ פעולות חיבור וכפל של מטריצות דלילות. חיבור מטריצות בגודל n X n לוקח בייצוג רגיל זמן O(n2). נניח כעת שיש m איברים שונים מהקבוע c במטריצה אחת ו-k בשניה. חיבור שתי המטריצות נעשה ע"י מיזוג שתי הרשימות המייצגות את המטריצות נ"ל. זמן המיזוג הוא אורכן הכולל של הרשימות כלומר O(m+k).כלומר עבור מטריצות ריבועיות דלילות זמן החיבור הוא o(n2). תרגיל ממבחן: הראו שכפל מטריצות ריבועיות דלילות בייצוג זה לוקח זמן o(n3) במקום O(n3). לשם כך נדרש מיון לפי עמודות של מטריצה B.

  18. תוכנית לחיבור מטריצות C=A+B int lex(NODE a, NODE b); /* lexicographically compares a and b; Returns: -1 if (a.row,a.col) < (b.row,b.col) 0 if (a.row,a.col) = (b.row,b.col) 1 if (a.row,a.col) > (b.row,b.col) */ typedef struct NODE { int row, col; float val; } NODE A[mA+1], B[mB+1], C[mA+mB+1]; void ADD(NODE *A, NODE *B, NODE *C, int *mC){ int i=0, j=0, *mc=0; A[mA]=B[mB]={+,+, 0}; while (i < mA || j < mB){ switch lex(A[i],B[j]){ case –1: C[*mC++] = A[i++]; break; case 1: C[*mC++] = B[j++]; break; case 0: C[*mC].row = A[i].row; C[*mC].col = A[i].col; C[*mC].val = A[i++].val + B[j++].val; if (C[*mC].val  0) *mC++; }}} cs,Technion

  19. head 7 2 6 5 רשימות מקושרות נזכר כעת כיצד מתבצע חיפוש, הכנסה, והוצאה (find, insert, delete) ברשימות מקושרות ונגדיר וריאציות עליהן. חסרונות בהשוואה למערך: יתרונות בהשוואה למערך: • אין גישה לפי אינדקס בזמן O(1). • מאפשר הקצאת זיכרון דינמית. • אין צורך להקצות מקום זיכרון רצוף. • ניתן להוציא איבר מתוך רשימה מקושרת בלי להשאיר "חור" כמו במערך ולכן ניתן לסרוק את כל האיברים בזמן ליניארי. cs,Technion

  20. head 7 2 5 t חיפוש ברשימה מקושרת • void init (NODE *head){ • (* head) = NULL; • } פעולתאיתחול init(head): • NODE * find (DATA_TYPE x, NODE *head){ • NODE *t; • for (t = head; t != NULL && t -> info != x; • t = t -> next ); • return t; • } פעולת חיפוש find(x,head): חיפוש איבר בזמן O(n) במקרה הגרוע. cs,Technion

  21. head 7 2 5 t 6 הכנסת איבר לרשימה מקושרת • int insert ( NODE *t, DATA_TYPE x){ • NODE *p; • if (t == NULL) return 0; • p = (NODE *) malloc (sizeof (NODE)); • p -> info = x; • p -> next = t -> next ; • t -> next = p ; • return 1; • } פעולת insert(t,x): הפרמטר t מצביע לצומת שאחריו מוסיפים צומת חדש. הכנסת איבר בזמן O(1) כאשר ידוע מקום ההכנסה. דוגמא: insert(t,6). cs,Technion

  22. 2 5 head 7 t 6 temp הוצאת איבר מרשימה מקושרת • delete ( NODE *t){ • NODE * temp ; • temp = t  next;/* מצביע לצומת שמורידים*/ • t  next = temp  next ; • free (temp) ; • } פעולת delete(t): הפרמטר t מצביע לצומת שלפני הצומת שמוציאים. הוצאת איבר בזמן O(1) כאשר ידוע מקום ההוצאה. cs,Technion

  23. 2 5 head 7 הוצאת איבר מראש רשימה מקושרת • int delete_first ( NODE **p_head){ • NODE * temp ; • if (*p_head = = NULL) return 0 ; • temp = *p_head; • *p_head = temp  next ; • free (temp) ; • return 1; /* success code */ • } פעולת delete_first(r): p_head תכנות זה מסורבל. מהו הפתרון לכך ? cs,Technion

  24. head 2 5 הכנסה לפני איבר ראשון זהה להכנסה לפני איבר כלשהו: head 2 5 1 רשימות עם כותרת • מוסיפים איבר "ריק" בתחילת הרשימה. תוספת זו חוסכת: • טיפול מיוחד ברשימות ריקות • טיפול מיוחד בזמן הכנסה לפני הצומת הראשון. בייצוג זה רשימה בת שתי איברים ממומשת כך: head ורשימה ריקה ממומשת כך: cs,Technion

  25. temp rear2 2 5 3 4 9 7 6 8 rear1 רשימות מעגליות 2 5 rear 9 6 7 • יתרונות: • אפשר להגיע מכל איבר לכל איבר • ניתן לשרשר רשימות מעגליות בזמן קבוע. cs,Technion

  26. 2 5 8 9 t t רשימה מקושרת דו-כיוונית 2 5 8 9 יתרון: מאפשר להוציא איבר בהינתן מצביע tאליו(ולא רק את האיבר שאחריו). t  next  prev = t  prev ; t  prev  next = t  next ; cs,Technion

  27. 8 5 9 t 8 8 9 t head head 2 2 הוצאה "טריקית" האם ניתן להוציא איבר אליו מצביע tגם מרשימה חד-כוונית ? פשוט נעתיק את האינפורמציה בתא העוקב ל-t לתא ש-t מצביע אליו ונוציא את התא העוקב. • מהן החסרונות של שיטה זו ? • צומת יכול להכיל הרבה אינפורמציה ולכן העתקה עלולה להיות פעולה ארוכה. • יתכן וישנם מצביעים נוספים לתא שהוצא ואין אפשרות לעדכן מצביעים אלה. cs,Technion

  28. שני נעלמים. דוגמא: 9 2 0 14 1 7 0 2 1 5 0 3 2 1 0 ייצוג פולינומים נעלם אחד. דוגמא: 0.3 9 4.5 2 0.1 0 חזקה מקדם מימוש זה יעיל כאשר ההפרש בין החזקה הגבוהה והנמוכה גדול וכן הרבה מקדמים של חזקות הביניים מתאפסים. cs,Technion

  29. 1 2 3 0 4 column next value row 0 1 down 2 3 4 ייצוג מטריצות דלילות מבנה צומת: • typedef struct node { • float value ; • struct node*next, *down ; • int row, column ; • } NODE ; cs,Technion

  30. כפל מטריצות דלילות כפל מטריצות C = AB. התוכנית מחשבת את האיבר Cij. #define N_row 20 #define N_col 20 NODE *row_header[N_row], *col_header[N_col]; /* Compute the value of c[i][j] */ double mult_row_col(int i, int j){ doublec = 0 ; NODE *r = row_header[i], *k = col_header[j]; while (r != NULL && k != NULL){ if (r  column < k  row) r = r  next ; else (r  column > k  row) k = k  down ; else /* r->column ==k->row */ c + = r  value * k  value ; r = r  next ; k = k  down ; } return c; } cs,Technion

More Related