מבוא למדעי המחשב רשימות תרגול 12: ורשומות מקושרות
רשומות רשומה (struct) היא אוסף של משתנים מטיפוסים זהים או שונים המקובצים יחד תחת שם אחד. האפשרות לאגד מספר משתנים ברשומה מאפשרת גישה מהירה ויעילה לנתונים שבתוכם.
תרגיל 1 כתוב תכנית הקולטת שני מספרים מרוכבים מהמשתמש, מדפיסה אותם ולאחר מכן מדפיסה את הסכום והמכפלה שלהם. בתרגיל ניצור באמצעות רשומות טיפוס חדש הטיפוס החדש הוא מספר מורכב. נגדיר פעולות על הטיפוס הזה כמו : חיבור, חיסור, ומכפלה. נזכיר כי מספר מורכב C הוא זוג של מספרים (a,b) ממשיים כך ש:. c = a + bi חיבור: (a + bi) + (c +di) = (a+c)+ (b+d)i חיסור: (a + bi) - (c +di) = (a-c)+ (b-d)i כפל: (a+bi)*(c+di) = (ac-bd)+ (bc+ad)i
פתרון תרגיל 1 #define PLUS 1 #define SUB -1 typedef struct { double real; double image; Complex ; /* Add or Subtract two complex numbers */ Complex CompOp(Complex,Complex,int optype); /* Multiply two complex numbers */ Complex CompMult(Complex,Complex); /* Read a complex number from the user */ Complex ReadComp(); /* Print a complex number on the screen */ void PrintComp(Complex c);
פתרון תרגיל 1 int main( ) { Complex c1,c2,c3 ; /* declare on 3 complex numbers */ c1 = ReadComp(); /* read c1 from the user */ c2 = ReadComp(); /* read c2 from the user */ PrintComp(c1); /* print c1 on the screen */ PrintComp(c2); /* print c2 on the screen */ c3 = CompOp(c1,c2,PLUS); /* c3 = c1 + c2 */ PrintComp(c3);/* print c3 on the screen */ c3 = CompMult(c3,c2); /* c3 = c3*c2 */ PrintComp(c3); /* print c3 on the screen */ return 0;
פתרון תרגיל 1 Complex CompOp(Complex c1,complex c2,int optype){ Complex cres ; cres.real = c1.real + optype*c2.real ; cres.image = c1.image + optype*c2.image ; return cres ; Complex CompMult(Complex c1,complex c2){ Complex cres ; cres.real = c1.real*c2.real-c1.image*c2.image ; cres.image = c1.image*c2.real + c1.real*c2.image ; return cres ;
Complex ReadComp(){ Complex c ; printf("please enter the real part:"); scanf("%lf",&c.real); printf("please enter the imaginary part:"); scanf("%lf",&c.image); return c ; void PrintComp(Complex c){ printf("%f+%fi\n",c.real,c.image); return ; פתרון תרגיל 1
מצביעים לרשומות כפי שהגדרנו מצביעים לטיפוסים שהכרנו עד כה מצביעים לרשומות. העברה לפונקציה: ניתן להגדיר גם כמו העברה של כל טיפוס אחר: מעבירים את כתובת הרשומה לפרמטר של הפונקציה, שיוגדר לצורך כך כמצביע לרשומה מאותו טיפוס. שימוש: כאשר נרצה לפנות לרשומה המוצבעת - מוסיפים * לפני שם המצביע. לדוגמא על מנת לפנות לשדה real המוצבע ע"י c. נכתוב: (*c).real דרך נוספת: שימוש באופרטור < - לדוגמא c->real
פתרון תרגיל 1 -באמצעות מצביעים דרך נוספת לממש את הפונקציה CompMult בעזרת מצביעים: Complex* CompMult(Complex *c1,complex *c2){ Complex *c3 ; if(!(c3 = (Complex*)malloc(sizeof(Complex))) ){ printf( Out of Memory\n ); exit(1); c3->real = c1->real*c2->real c1->image*c2->image ; c3->image = c1->real*c2->image + c1->image*c2->real; return c3 ; Complex c1,c2,*c3 ; c3 =CompMult(&c1,&c2); הקריאה לפונקציה :
דוגמא מאגר שמות נממש מאגר שמות שיחזיק את שמותיהם של אנשים, ואת כמות השמות ע"י שימוש במבנה הבא: #define MAX_SIZE 15 typedef struct{ char fname[max_size]; /* first name */ char lname[max_size]; /* last name */ Name; typedef struct{ Name* names; int count; NamesDB; /* Names Data Base */ כשלמשתמש ניתנת האפשרות לבחור את גודל המאגר.
int main(){ NamesDB db; int i; printf( Please enter the DB size: ); scanf( %d\n, &db.count); דוגמא מאגר שמות db.names = (Name*) malloc(db.count*sizeof(name)); for(i=0; i<db.count; i++) scanf( %s %s,db.names[i].fname, db.names[i].lname); for(i=0; i<db.count; i++) printf( %d. %s %s\n, i+1, db.names[i].fname, db.names[i].lname); free( db.names ); return 0; הקצאת מערך השמות וקליטת השמות לתוכו מעבר על מערך השמות והדפסת השמות שחרור מערך השמות
דוגמא חיפוש לפי שם משפחה פונקציה שמקבלת מצביע למאר שמות ושם משפחה, ומחזירה את האינדקס של השם האדם הראשון במאגר שיש לו את שם המשפחה (אם לא קיים, יוחזר 1-): int search_lname(namesdb* db, char* lname) { int i; for(i=0; i<db->count; i++) if(!strcmp(db->names[i].lname, lname) ) return i; return -1;
רשימות מקושרות
רשימות מקושרות רשימה מקושרת מורכבת מרשומות הכוללות שדות מידע ושדה שהוא מצביע לרשומה מאותו סוג [נשים לב שהמצביע הוא מצביע לאותו טיפוס מבנה בו הוא מוגדר] שרשרת הרשומות משמשת כמו מערך של רשומות אבל: אין חובה להגדירם ברצף בזיכרון אין חובה לציין את מספרם מראש כך נקבל מבנה נתונים דינאמי בו נוכל להכניס ולהוציא אברים בקלות מימוש: נחזיק מצביע לראש הרשימה head רשומה האחרונה ברשימה תצביע ל NULL
תרגיל 2 כתבו פונקציה המקבלת רשימה מקושרת, בה לפחות איבר אחד וידוע כי האיבר האחרון בה מצביע לNULL נתונות ההגדרות הבאות למבנה הצומת ברשימה המקושרת: typedef struct cell *CellPtr; typedef struct cell { int contents; CellPtr next; Cell; על הפונקציה להדפיס את איברי הרשימה בסדר הפוך.
פתרון תרגיל 2 void rev_prt (CellPtr head){ /* stop condition */ if(head == NULL){ return; rev_prt(head->next); printf("%d ", head->contents); return;
תרגיל 3 הגדירו פונקציה רקורסיבית בשם equal המקבלת שני מצביעים לשתי רשימות מקושרות ומחזירה 1 אם תוכנן של שתי הרשימות זהה ו 0 אחרת. נתונות ההגדרות הבאות למבנה הצומת ברשימה המקושרת: typedef struct node { int data; struct node * next; Node; int equal(node * list1, Node * list2); חתימת הפונקציה:
פתרון תרגיל 3 int equal(node * list1, Node * list2){ if(list1==null && list2==null){ return 1; if(list1==null list2==null){ return 0; return (list1->data==list2->data)&&equal(list1->next,list2->next);
תרגיל 4 כתבו פונקציה בשם printmid המקבלת רשימה מקושרת, בה לפחות איבר אחד וידוע כי האיבר האחרון בה מצביע ל.NULL על הפונקציה להדפיס את ערכו של האיבר האמצעי ברשימה. יש לעבור על הרשימה פעם אחת בלבד!!! נתונות ההגדרות הבאות למבנה הצומת ברשימה המקושרת: typedef struct node { int data; struct node * next; Node;
- הרעיון תרגיל 4 נתקדם ברשימה המקושרת עם שני מצביעים בו- זמנית. מצביע אחד, p1, יקודם כל פעם לצומת הבא ברשימה המקושרת ומצביע שני, המקושרת.,p2 יקודם כל פעם שני צמתים ברשימה כך שבסיום המעבר על הרשימה ע"י המצביע p2 המצביע p1 יצביע על האיבר האמצעי ברשימה.
void printmid(node * head){ Node *p1, *p2; p2 = p1 = head; while(p2 && p2->next){ p1=p1->next; p2=p2->next->next; printf("the mid is %d\n", p1->data); return; פתרון תרגיל 4
תרגיל 5 נתונה הרשומה הבאה המתארת איבר ברשימה דו כיוונית: typedef struct node{ int num; struct node *next; struct node *prev; Node; כתבו פונקציה המקבלת שני מצביעים לתחילת שתי רשימות מקושרות באותו אורך מן הסוג הנ"ל. ומשלבת את אברי הרשימה השנייה בראשונה בסדר הפוך כאשר האיבר הראשון ברשימה החדשה יהיה האיבר הראשון ברשימה הראשונה, האיבר השני יהיה האיבר האחרון מהרשימה השנייה וכך הלאה. אפשר לשנות את הקלט.
תרגיל 5 עבור: למשל, הפונקציה תיצור את הרשימה הבאה:
פתרון תרגיל 5 void merge(node* list1, Node* list2){ Node *p, *q, *r, *s; p = list1; q = list2; while(q->next!= NULL) /* q will point to the end of list2 */ q = q->next; while(p->next!= NULL) /* insert the node from list2 to list1*/{ r = p->next; s = q->prev; q->next = p->next; q->next->prev = q; p->next = q; q->prev = p; p = r; q = s; q->next = NULL; /* dealing with lists of size one */ p->next = q; q->prev = p; return;
תרגיל 6 כתבו פונקציה בשם split המקבלת רשימה מקושרת, בה לפחות איבר אחד וידוע כי האיבר האחרון בה מצביע ל NULL על הפונקציה להחזיר (דרך רשימת הארגומנטים שלה) שתי רשימות: 1. רשימת האברים בקלט ששדה info שלהם זוגי 2. רשימת האברים בקלט ששדה info שלהם אי זוגי אין לפגוע בקלט. בשאלה זו ניתן לממש ולהשתמש בפונקציות עזר. נתונות ההגדרות הבאות למבנה הצומת ברשימה המקושרת: typedef struct node { int info; struct node *next; Node;
פתרון תרגיל 6 מכיוון שאין לפגוע בקלט, עלינו ליצור 2 רשימות חדשות. על מנת להקצות זיכרון לאיבר ברשימה חדשה, נגדיר פונקצית עזר.new_node Node* new_node(int info){ Node* node; if (NULL==(node=(Node*)malloc(sizeof(Node)))) { printf("memory allocation error\n"); exit(1); node->info=info; node->next=null; return node;
void split_lists(node* list, Node** list_odd, Node** list_even){ Node *p_odd, *p_even, *p = list; *list_even=null;*list_odd=null; while(null!=p) { if ((p->info)%2) { p_odd=*list_odd; *list_odd=new_node(p->info); (*list_odd)->next=p_odd; else { p_even=*list_even; *list_even=new_node(p->info); (*list_even)->next=p_even; p=p->next; פתרון תרגיל 6