Presentation is loading. Please wait.

Presentation is loading. Please wait.

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

Similar presentations


Presentation on theme: "מערכים, מטריצות דלילות, ורשימות מקושרות"— Presentation transcript:

1 מערכים, מטריצות דלילות, ורשימות מקושרות
Lecture2 of Geiger & Itai’s slide brochure מערכים, מטריצות דלילות, ורשימות מקושרות חומר קריאה לשיעור זה 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 מימושים למערך (1) אזור זיכרון רצוף: כל הפעולות מתבצעות בזמן O(1).
מימושים למערך (1) address 10243 10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 10258 10259 10260 10242 10241 10240 10261 10262 אזור זיכרון רצוף: כל הפעולות מתבצעות בזמן O(1). base 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 offset הפעולות: מתבססות על חישובי כתובות Base+offset cs,Technion

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

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

6 מערך רב-ממדי (1) i3n2 n1 + i2n1 + i1 גודל אורך מישור שורה
במערך דו-ממדי: 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 i1 n1 i2 n2 1 2 i3n2 n1 + i2n1 + i1 גודל מישור אורך שורה הערה: החישוב מניח קונוונצית ציינים ב-C (a[0] הוא המקום הראשון)

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

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

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

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

13 הקוד לאיתור זבל 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 המציינים מצביעים V ערכים לאזור הבטוח המוגדרים A B C 5 4 3 2 1 333 7 222 111 3 5 131 224 554 675 234 1 2 3 4 5 234 2 554 1 234 675 554 1 131 4 1 123 643 453 2 3 5 top מציינים מוגדרים זבל

14 הקוד לפעולות המערך top = 0; אתחל init(V,const): constant = 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]. n 3 2 1 6 5 4 10 9 8 7 הייצוג: אוסף וקטורים המאוחסנים במערך רציף יחיד: מהי הכתובת של איבר 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 6 5 4 3 2 1 8 בדוגמא זו 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
typedef struct NODE { int row, col; float val; } NODE A[mA+1], B[mB+1], C[mA+mB+1]; int lex(NODE a, NODE b); /* lexicographically compares a and b; Returns: 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) */ 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 רשימות מקושרות נזכר כעת כיצד מתבצע חיפוש, הכנסה, והוצאה (find, insert, delete) ברשימות מקושרות ונגדיר וריאציות עליהן. 7 head 2 6 5 חסרונות בהשוואה למערך: יתרונות בהשוואה למערך: אין גישה לפי אינדקס בזמן O(1). מאפשר הקצאת זיכרון דינמית. אין צורך להקצות מקום זיכרון רצוף. ניתן להוציא איבר מתוך רשימה מקושרת בלי להשאיר "חור" כמו במערך ולכן ניתן לסרוק את כל האיברים בזמן ליניארי. cs,Technion

20 חיפוש ברשימה מקושרת פעולת איתחול init(head): פעולת חיפוש find(x,head):
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) במקרה הגרוע. 7 head 2 5 t cs,Technion

21 הכנסת איבר לרשימה מקושרת
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). 7 head 2 6 5 t cs,Technion

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

23 הוצאת איבר מראש רשימה מקושרת
הוצאת איבר מראש רשימה מקושרת 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): 7 head 2 5 p_head תכנות זה מסורבל. מהו הפתרון לכך ? cs,Technion

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

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

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

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

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

29 ייצוג מטריצות דלילות מבנה צומת: column row value next down
1 2 3 4 column row value next down 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){ double c = 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


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

Similar presentations


Ads by Google