6-1 ΜΑΘΗΜΑ 6 ο Ανασκόπηση σε Δείκτες, Συνδεδεμένες Λίστες, Ουρές, Στοίβες
6-2 Δείκτες (pointers) - Ανασκόπηση(i) Μεταβλητές –που δεν περιέχουν μια τιμή κάποιου απλού τύπου δεδομένων –αλλά τη διεύθυνση μιας άλλης μεταβλητής Παράδειγμα int x, *p; x δ124 p δ δ124
6-3 Δείκτες (pointers)(ii) Δεικτοδότηση: & –η διεύθυνση μιας μεταβλητής p = &x; Αποδεικτοδότηση: * –το περιεχόμενο μιας διεύθυνσης printf("%d", *p); *p = 7; x δ124 p δ δ124
6-4 Πίνακες και δείκτες(i) Αριθμητική δεικτών int a[3] = {7, 6, 42}; int *p; p δ247 a δ124 7 δ125 6 δ δ124 p = &(a[0]); p = &a; p = a; printf("%d\n", *p); printf("%d\n", *(p+1)); p = p+2; printf("%d\n", *p);
6-5 Πίνακες και δείκτες(ii) Ισοδυναμία πινάκων και δεικτών –Ένας πίνακας είναι ένας δείκτης στο πρώτο στοιχείο. –a[i] ισοδύναμο με *(a+i) –Οι πίνακες όμως είναι σταθεροί δείκτες, δηλαδή δεν μπορούν να αλλάξουν τιμή int a[3] = {7, 6, 42}; int *p = &a; p++; /* correct */ a++; /* wrong! */
6-6 Πίνακες και δείκτες(iii) Παράδειγμα: αντιγραφή πινάκων int a[10], b[10]; int *p = a, *q = b, i; /* assume b initialized */ for (i=0; i<10; i++) *p++ = *q++;
6-7 Πίνακες και δείκτες(iv) Παράδειγμα: εκτύπωση συμβολοσειράς void putchar (char c); char s[] = "Hello world!\n"; char *p; for (p = s; *p != '\0'; p++) putchar(*p);
6-8 Κενός δείκτης και δείκτης σε κενό Ο κενός δείκτης: NULL –Απαγορεύεται να αποδεικτοδοτηθεί! int *p = NULL; *p = 42; /* wrong! */ –Ο μόνος δείκτης που αντιστοιχεί στην ψευδή λογική τιμή, όταν χρησιμοποιείται σε συνθήκη Ο δείκτης σε κενό: void * –Γενική μορφή δείκτη –Απαγορεύεται να αποδεικτοδοτηθεί! –Απαγορεύεται η αριθμητική δεικτών!
6-9 Δείκτες αντί περάσματος με αναφορά Κώδικας Pascal procedure inc (var x : integer); begin x := x+1 end;... inc(a)... Ισοδύναμος κώδικας C void inc (int * ptr_x) { (*ptr_x)++; }... inc(&a)...
6-10 Δείκτες σε εγγραφές Συντομογραφία p->x είναι ισοδύναμο με (*p).x Παράδειγμα struct { int x, y; } coordinates, *p; coordinates.x = 1; coordinates.y = 3; p = &coordinates; printf("%d\n", p->x); printf("%d\n", p->y);
6-11 Μετατροπές τύπων(i) Έμμεσες (coercions) double d = 3; (με ανάθεση) int x = 3.14; int f (int x); (με πέρασμα παραμέτρου) f(3.14); Άμεσες (type casting) ( τύπος ) έκφραση (double) 3 (int) 3.14 (int *) NULL
6-12 Μετατροπές τύπων(ii) Πρόβλημα: πώς μπορώ να τυπώσω το αποτέλεσμα της πραγματικής διαίρεσης δυο ακεραίων αριθμών, χωρίς νέες μεταβλητές; int x = 5, y = 3; Λάθος λύση #1: printf("%d", x/y); Λάθος λύση #2: printf("%lf", x/y); Σωστές λύσεις: printf("%lf", 1.0 * x / y); printf("%lf", (double) x / (double) y); printf("%lf", (double) x / y);
6-13 Δυναμική παραχώρηση μνήμης(i) Συναρτήσεις βιβλιοθήκης –void * malloc (size_t n); Δυναμική παραχώρηση μνήμης μήκους n bytes. Το αποτέλεσμα πρέπει να μετατραπεί στο σωστό τύπο. Επιστρέφεται NULL αν εξαντληθεί η μνήμη. –void free (void * p); Αποδέσμευση της μνήμης στην οποία δείχνει το p. Το p πρέπει να έχει δημιουργηθεί με προηγούμενη κλήση στη malloc. Πόσα bytes χρειάζονται; –sizeof( type ) π.χ. sizeof(int) –sizeof( variable ) π.χ. sizeof(x)
6-14 Δυναμική παραχώρηση μνήμης(ii) Παράδειγμα int *p; int i; p = (int *) malloc(sizeof(int)); *p = 42; free(p); p = (int *) malloc(10 * sizeof(int)); for (i = 0; i < 10; i++) p[i] = 42; free(p); Το αποτέλεσμα της malloc πρέπει να ελεγχθεί ότι δεν είναι NULL !
6-15 ΔΟΜΕΣ & ΔΕΙΚΤΕΣ Ας θεωρήσουμε την παρακάτω δήλωση τύπου typedef struct { int idNumber; char descr[100]; int avail; } sparePartType; Και τις δηλώσεις μεταβλητών: sparePartType var1 = (123, “Wheel”, 1); /* Αρχικές τιμές */ sparePartType *var2 = (sparePartType *) malloc(sizeof(sparePartType); Για να αποθέσουμε τιμές ή να χρησιμοποιήσουμε τα πεδία μπορούμε να γράψουμε: *var2 = var1; /* Αποθέτουμε όλες τις τιμές των πεδίων από την var1 στη θέση μνήμης που «δείχνει η var2 */ var2->idNumber = 10; /* Σύνταξη όταν χρησιμοποιούμε δείκτες σε δομές */ var1.idNumber = 20; /* Σύνταξη όταν χρησιμοποιούμε απλές μεταβλητές δομών */
6-16 ΔΟΜΕΣ & ΔΙΑΝΥΣΜΑΤΑ Οι δομές και τα διανύσματα μπορούν να συνδυασθούν χωρίς περιορισμούς π.χ. typedef struct { char firstName[10]; char lastName[10]; } personType; typedef struct { personType aPerson; int id; int age; int sex; } classType; Οπότε μπορούμε να ορίσουμε μία τάξη μαθητών σαν ένα διάνυσμα στοιχείων τύπου classType. Εάν υποθέσουμε ότι η τάξη μας έχει 10 άτομα τότε ορίζεται σαν μία μεταβλητή π.χ. Class101Var classType class101Var[10]; Σε κάθε «κελί» του διανύσματος υπάρχει μία δομή tύπου classType. Για να βρούμε το μικρό όνομα του τρίτου μαθητή class101Var[2], γράφουμε: (class101Var[2].aPerson).firstName
6-17 ΔΟΜΕΣ & ΔΙΑΝΥΣΜΑΤΑ & ΔΕΙΚΤΕΣ typedef struct { char firstName[10]; char lastName[10]; int id; int age; int sex; } personType; typedef struct { char * classId; personType *aPerson; } classType; Οπότε μπορούμε να ορίσουμε μία τάξη μαθητών σαν ένα διάνυσμα στοιχείων τύπου classType. Εάν υποθέσουμε ότι η τάξη μας έχει 10 άτομα τότε ορίζεται σαν μία μεταβλητή π.χ. Class101Var classType class101Var[10]; classType initializeClass(classType class101Var) { for (int i = 0; i< 10; i++) { class101Var[i].classId = malloc(7); strcpy(class101Var[i].classId, “ECE101”); class101Var[i].aPerson = (personType *) malloc(sizeof(personType)); strcpy((class101Var[i].aPerson) firstName, “”); strcpy(class101Var[i].aPerson lastName, “”); (class101Var[i].aPerson) id = 0; class101Var[i].aPerson age = 0; class101Var[i].aPerson sex = 0; return(class101Var); }
6-18 ΠΑΡΑΔΕΙΓΜΑ typedef struct { int F1; int F2; …….. int F8 } aStructType; aStructType *structArray[10]; structArray[0] = (aStructType *) malloc(sizeof(aStructType)); structArray[0] F1 = 10; *structArray[0].F2 = 20; …………… F1F2F3F4F5F6F7F8 StructArray[ ] aStructType
6-19 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 1 Μέχρι τώρα έχουμε μιλήσει για απλές κατασκευές δεδομένων όπως δομές, διανύσματα, και πίνακες Επίσης χρησιμοποιήσαμε αυτές τις απλές κατασκευές για να ορίσουμε πιο σύνθετες κατασκευές όπως διανύσματα από δομές, διανύσματα από δείκτες σε δομές κλπ. Το κοινό χαρακτηριστικό των κατασκευών που είδαμε μέχρι τώρα είναι ότι το μέγεθός τους ορίζεται στατικά κατά την σχεδίαση του προγράμματος. Για παράδειγμα τα διανύσματα έχουν πεπερασμένο μήκος, μέγεθος, και διαστάσεις Το ζητούμενο είναι να ορίσουμε κατασκευές (π.χ. Λίστες) για τις οποίες δεν χρειάζεται να ορίσουμε πόσα στοιχεία έχουν, αλλά θα μπορούμε να προσθέτουμε όσα στοιχεία θέλουμε δυναμικά, δηλαδή κατά την εκτέλεση, και σύμφωνα με τις ανάγκες του προγράμματος μας
6-20 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 2 Πριν όμως προχωρήσουμε στην παρουσίαση των διασυνδεόμενων λιστών ας κάνουμε μία μικρή επανάληψη στις δομές Η παρακάτω κατασκευή ορίζει ένα μία δομή με τρία πεδία (fields). struct { int id; char * name; char * surname; } Γραφικά η παραπάνω δομή θα μπορούσε να παρασταθεί ως εξής: int idchar* namechar * surname
6-21 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 3 Εάν θέλαμε να φτιάξουμε μία κατασκευή π.χ. το μοντέλο μίας τάξης από μαθητές, ένας τρόπος θα ήταν να ορίσουμε ένα διάνυσμα από δομές σαν και αυτή που ορίσαμε παραπάνω, ή ακόμη καλύτερα ένα διάνυσμα από δείκτες σε τέτοιες δομές. Σχηματικά οι δύο αυτές περιπτώσεις θα φαίνονται ως εξής: F3F2F1 ClassArray_2[9] F1 F2 F3 Δομή Διάνυσμα classArray_1[9] Δομή
6-22 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 4 Ας δούμε όμως τώρα τι θα γινόταν εάν εισάγαμε σε μία δομή ένα νέο πεδίο το οποίο θα ήταν ένας δείκτης σε δομή του ιδίου τύπου! Ας υποθέσουμε λοιπόν τα εξής: struct Student { int id; char * name; char * surname; Student *next; }; int idchar *namechar *surname Student * next
6-23 ΔΙΑΣΥΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 5 Ας θεωρήσουμε τώρα μια κατασκευή που μοιάζει όπως το παρακάτω σχήμα: int idchar *namechar *surname Student * next int idchar *namechar *surname Student * next int idchar *namechar *surname Student * next NULL struct Student { int id; char *name; char *surname; Student *next; }; typedef struct Student StudentType; StudentType *list_first = NULL, *list_second, *list_third; list_first = (StudentType *) malloc(sizeof(Student)); list_first->next = (StudentType *) malloc(sizeof(Student)); list_second = list_first->next; list_second->next = (StudentType *) malloc(sizeof(Student)); list_third = list_second->next; list_third->next = NULL;
6-24 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 6 struct Student { int id; char *name; char *surname; Student *next; }; typedef struct Student StudentType; StudentType *list_first, *list_second, *list_third; list_first = (StudentType *) malloc(sizeof(StudentType)); list_first->next = (StudentType *) malloc(sizeof(StudentType)); list_second = list_first->next; list_second->next = (StudentType *) malloc(sizeof(StudentType)); list_third = list_second->next; list_third->next = NULL; * list_first * list_second int idchar *namechar *surname Student * next int idchar *namechar *surname Student * next int idchar *namechar *surname Student * next * list_third NULL
6-25 ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ - 7 int idchar *namechar *surname Student * next int idchar *namechar *surname Student * next Int idchar *namechar *surname Student * next * temp NULL * list * temp struct Student { int id; char *name; char *surname; Student *next; }; typedef struct Student StudentType; StudentType *list, *temp; int i; list = (StudentType *) malloc(sizeof(StudentType)); temp = list; for(i=1; i<3; i++) { temp->next = (StudentType *) malloc(sizeof(StudentType)); temp = temp->next; temp->next = NULL; } * temp NULL
6-26 ΠΑΡΑΛΛΑΓΕΣ ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΩΝ Μέχρι τώρα είδαμε διασυνδεόμενες λίστες όπου οι κόμβοι είναι ενωμένοι από τον πρώτο κόμβο (κεφαλή της λίστας) προς τον τελευταίο (ουρά της λίστας) Υπάρχουν αρκετές παραλλαγές αυτής της απλής τοπολογίας. Μερικές και πιο συνήθεις από αυτές τις παραλλαγές είναι: – Π1. Ο τελευταίος κόμβος συνδέεται με τον πρώτο (κυκλικές λίστες) –Π2. Ο κάθε κόμβος συνδέεται με τον επόμενο και με τον προηγούμενο –Π3. Υπάρχουν κάποιοι εξωτερικοί κόμβοι που είναι δείκτες σε συγκεκριμένους (και πιθανόν σημαντικούς) κόμβους της λίστας.
6-27 ΠΑΡΑΛΛΑΓΗ 1 (Σχηματικά) Σε αυτή τη παραλλαγή ο τελευταίος κόμβος είναι συνδεδεμένος με τον πρώτο. Σε αυτή τη περίπτωση ο πρώτος και (εάν θέλουμε) και ο τελευταίος κόμβος θα πρέπει να χαρακτηρίζονται κατάλληλα ώστε να γνωρίζουμε ποιοι είναι αυτοί οι κόμβοι. Αυτό επιτυγχάνεται με τη χρήση δύο μεταβλητών (π.χ. first, last) που είναι δείκτες στον πρώτο και τελευταίο κόμβο αντίστοιχα. firstlast
6-28 ΠΑΡΑΛΛΑΓΗ 1 (Κώδικας –Δύο Κόμβοι) typedef struct Rec_tag { char data[21]; Rec_tag *next; } Rec; typedef Rec *RecPointer; void main() { RecPointer r1, r2, first, last; r1 = (RecPointer)malloc(sizeof(Rec)); r1->next = r1; first = r1; last = r1; r2 = (RecPointer)malloc(sizeof(Rec)); r1->next = r2; last = r2; r2->next = first; }
6-29 ΠΑΡΑΔΕΙΓΜΑ ΠΑΡΑΛΛΑΓΗ 1 – ΔΙΑΓΡΑΦΗ ΤΕΛΕΥΤΑΙΟΥ ΚΟΜΒΟΥ typedef struct Rec_tag { char data[21]; char data[21]; Rec_tag *next; Rec_tag *next; } Rec; typedef Rec *RecPointer; RecPointer deleteLast(RecPointer first, RecPointer last) { RecPointer current = first; RecPointer current = first; while (current->next != last) while (current->next != last) current = current->next; current = current->next; current->next = first; current->next = first; free(last); free(last); last = current; last = current; }
6-30 ΠΑΡΑΛΛΑΓΗ 2.1 (Σχηματικά) Σε αυτή τη παραλλαγή ο κάθε κόμβος είναι συνδεδεμένος με τον προηγούμενο. Σε αυτή τη περίπτωση ο πρώτος κόμβος θα πρέπει να χαρακτηρίζεται κατάλληλα ώστε να γνωρίζουμε πια είναι η κεφαλή της λίστας. Αυτό επιτυγχάνεται με τη χρήση μίας μεταβλητής (π.χ. first) που είναι δείκτης στον πρώτο κόμβο. first NULL previous next
6-31 ΠΑΡΑΛΛΑΓΗ 2.1 (Κώδικας – Δύο Κόμβοι) typedef struct Rec_tag { char data[21]; Rec_tag *next; Rec_tag *previous; } Rec; typedef Rec *RecPointer; void main() { RecPointer r1, r2, first, last; r1 = (RecPointer)malloc(sizeof(Rec)); r1->next = NULL; r1->previous = NULL; first = r1; r2 = (RecPointer)malloc(sizeof(Rec)); r1->next = r2; r2->next = NULL; r2->previous = r1; }
6-32 ΠΑΡΑΔΕΙΓΜΑ ΠΑΡΑΛΛΑΓΗ 2.1 – ΕΙΣΑΓΩΓΗ ΠΡΙΝ ΑΠΟ ΚΑΠΟΙΟ ΚΟΜΒΟ typedef struct Rec_tag{ char data[21]; char data[21]; Rec_tag *next; Rec_tag previous; Rec_tag *next; Rec_tag previous; } Rec; typedef Rec *RecPointer; RecPointer InsertBefore(RecPointer first, RecPointer beforeNode, RecPointer aNewNode) { RecPointer current = first; RecPointer current = first; while (current != beforeNode && current->next != NULL) while (current != beforeNode && current->next != NULL) current = current->next; current = current->next; if(current == beforeNode) { if(current == beforeNode) { aNewNode->next = beforeNode; aNewNode->next = beforeNode; aNewNode->previous = beforeNode->previous; aNewNode->previous = beforeNode->previous; beforeNode->previous->next = aNewNode; beforeNode->previous->next = aNewNode; beforeNode->previous = aNewNode; beforeNode->previous = aNewNode; } elseif(current->next == NULL) elseif(current->next == NULL) current->next = aNewNode current->next = aNewNode aNewNode->previous = current aNewNode->previous = current aNewNode-next = NULL; aNewNode-next = NULL; }
6-33 ΠΑΡΑΛΛΑΓΗ 2.2 (Σχηματικά) Σε αυτή τη παραλλαγή ο τελευταίος κόμβος είναι συνδεδεμένος με τον πρώτο και κάθε κόμβος είναι συνδεδεμένος με τον προηγούμενο. Σε αυτή τη περίπτωση ο πρώτος και (εάν θέλουμε) και ο τελευταίος κόμβος θα πρέπει να χαρακτηρίζονται κατάλληλα ώστε να γνωρίζουμε ποιοι είναι αυτοί οι κόμβοι. Αυτό επιτυγχάνεται με τη χρήση δύο μεταβλητών (π.χ. p1, p2) που είναι δείκτες στον πρώτο και τελευταίο κόμβο αντίστοιχα. firstlast
6-34 ΠΑΡΑΛΛΑΓΗ 2.2 (Κώδικας–Δύο Κόμβοι) typedef struct Rec_tag { char data[21]; Rec_tag *next; Rec_tag *previous; } Rec; typedef Rec *RecPointer; void main() { RecPointer r1, r2, first, last; r1 = (RecPointer)malloc(sizeof(Rec)); r1->next = NULL; r1->previous = NULL; first = r1; r2 = (RecPointer)malloc(sizeof(Rec)); last = r2; r1->next = r2; r2->next = NULL; r1->previous = r2; r2->previous = r1; }
6-35 ΠΑΡΑΔΕΙΓΜΑ ΠΑΡΑΛΛΑΓΗ 2.2 – ΕΙΣΑΓΩΓΗ ΠΡΙΝ ΑΠΟ ΚΑΠΟΙΟ ΚΟΜΒΟ typedef struct Rec_tag { char data[21]; char data[21]; Rec_tag *next; Rec_tag *next; } Rec; typedef Rec *RecPointer; RecPointer InsertBefore(RecPointer first, RecPointer last, RecPointer beforeNode, RecPointer aNewNode) { RecPointer aNewNode) { RecPointer current = first; RecPointer current = first; while (current != beforeNode && current != last) while (current != beforeNode && current != last) current = current->next; current = current->next; if(current == beforeNode) { if(current == beforeNode) { aNewNode->next = beforeNode; aNewNode->next = beforeNode; aNewNode->previous = beforeNode->previous; aNewNode->previous = beforeNode->previous; (beforeNode->previous)->next = aNewNode; (beforeNode->previous)->next = aNewNode; beforeNode->previous = aNewNode; beforeNode->previous = aNewNode; } elseif(current == last) elseif(current == last) current->next = aNewNode current->next = aNewNode aNewNode->previous = current aNewNode->previous = current aNewNode-next = first; aNewNode-next = first; }
6-36 ΠΑΡΑΛΛΑΓΗ 3 (Σχηματικά) Σε αυτή τη παραλλαγή υπάρχουν εξωτερικοί κόμβοι που «δείχνουν» σε σημαντικούς κόμβους της λίστας (όπως ο πρώτος κόμβος και ο τελευταίος). Αυτή η παραλλαγή χρησιμοποιείται όταν θέλουμε να μαρκάρουμε συγκεκριμένους κόμβους της λίστας. Προφανώς, όλες οι παραπάνω παραλλαγές μπορούν να συνδυασθούν και να παράγουν πιο σύνθετες κατασκευές. Η ιδέα είναι να σχεδιάζουμε κατασκευές οι οποίες επιτρέπουν την πιο γρήγορη εκτέλεση του αλγόριθμού μας.....
6-37 ΠΑΡΑΛΛΑΓΗ 3 (Κώδικας–Δύο Κόμβοι) typedef struct Rec_tag { char data[21]; Rec_tag *next; Rec_tag *previous; } Rec; typedef Rec *RecPointer; typedef struct External_tag { char info[21]; External_tag *ptrToListNode; } External; typedef External *ExternalPointer; void main() { RecPointer r1, r2; ExternalPointer ext1, ext2; r1 = (RecPointer)malloc(sizeof(Rec)); ext1 = (ExternalPointer)malloc(sizeof(External)); r1->next = NULL; r2 = (RecPointer)malloc(sizeof(Rec)); ext2 = (ExternalPointer)malloc(sizeof(External)); r1->next = r2; r2->next = NULL; ext1->ptrToListNode = r1; ext2->ptrToListNode = r2; }
6-38 ΟΥΡΕΣ Ο ΑΤΔ Ουρά περιγράφει μία «λίστα» αντικειμένων η οποία όμως έχει συγκεκριμένο τρόπο λειτουργίας για την εισαγωγή νέων στοιχείων και την εξαγωγή στοιχείων που υπάρχουν ήδη στη λίστα. Ο ΑΤΔ Ουρά έχει λοιπόν την εξής λειτουργία για την εισαγωγή νέων στοιχείων και την εξαγωγή στοιχείων που υπάρχουν ήδη στη λίστα: –Τα νέα στοιχεία τοποθετούνται πάντα στο τέλος της «λίστας» –Το μόνο στοιχείο το οποίο μπορεί να εξαχθεί από τη λίστα κάθε φορά είναι το πρώτο στοιχείο της λίστας (το στοιχείο της κεφαλής της λίστας)
6-39 Ο ΑΤΔ Ουρά Χαρακτηριστικά: –Μήκος Έγγυρες Πράξεις –Δημιουργία() : Ουρά –Εύρεση_Μήκους(Ουρά) : ακέραιος –Είναι_Κενή?(Ουρά): λογικό –Εισαγωγή(Ουρά, στοιχείο) : Ουρά –Ανάκτηση(Ουρά) : στοιχείο –Αντιγραφή(Ουρά, Ουρά) : Ουρά –Σύγκριση(Ουρά, Ουρά) : λογικό
6-40 ΥΛΟΠΟΙΗΣΗ ΟΥΡΑΣ ΜΕ ΔΙΑΣΥΝΔΕΟΜΕΝΕΣ ΛΙΣΤΕΣ #include struct Node { int data; Node *next; Node *previous; }; typedef struct Node NodeType; typedef struct { char queueName[10]; NodeType *last; } QueueType; void initialize(char Name[], QueueType *q); void enqueue(QueueType *q, int k); NodeType *dequeue(QueueType *q); queueName last next data prev NULL Πρώτος Κόμβος – Κεφαλή της Ουράς Τελευταίος Κόμβος next data prev NULL
6-41 ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΤΟΥ ΑΤΔ ΟΥΡΑ void initialize(char name[]) { QueueType *q = (QueueType *) malloc(sizeof(QueueType)); q->last = NULL; strcpy(q->queueName, name); } NodeType *dequeue(QueueType *q) { NodeType *curent = q->last; NodeType *before = q->last; if(current != NULL && current->next == NULL) { /* One element */ q->last = NULL; return current; } elseif (current != NULL) { /* Several elements */ while(current != NULL) before = current; current = current->next; } before->previous->next = NULL; return before; } else /* Empty list */ return NULL; }
6-42 ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΤΟΥ ΑΤΔ ΟΥΡΑ QueueType *enqueue(QueueType *q, int k) { NodeType *newNode = (NodeType *)malloc(sizeof(NodeType)); newNode->data = k; newNode->next = q->last; q->last->previous = newNode; q->last = newNode; return q; }
6-43 ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΤΟΥ ΑΤΔ ΟΥΡΑ QueueType *copy (QueueType *source, QueueType *dest) { /* Copies source queue into a new empty queue */ NodeType *sourceNode = source->last; /* Current node in original list */ NodeType *new, *destNode; /* The new node in the new list, and the current node in the new list */ int count = 1; if(dest->last != NULL) { fprintf(stderr, “Destination is not empty list\n”); exit(1); }; while(sourceNode!= NULL) { new = (NodeType *) malloc(sizeof(NodeType)); if(count == 1) { /* First node to be copied */ dest->last = new; dest->previous = NULL; destNode = new; count = 2; } destNode->next = new; new->previous = destNode; new->next = NULL; } return dest;
6-44 ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΤΟΥ ΑΤΔ ΟΥΡΑ int length(QueueType *q) { int size = 0; nodeType *curr = q->last; while(curr != NULL) { size = size +1; curr = curr->next; } return size; } int isEmpty?(QueueType *q) { if(q->last == NULL) return 1; else return 0;}
6-45 ΟΙ ΛΕΙΤΟΥΡΓΙΕΣ ΤΟΥ ΑΤΔ ΟΥΡΑ int areEqual?(QueueType *q1, QueueType *q2) { NodeType *node1 = q1->last; NodeType *node2 = q2->last; if(length(q1) != length(q2) /* Lists do not have equal size */ return 0; elseif (q1->last == NULL && q2->last == NULL) /* lists are empty */ return 1; else { /* lists have equal size and are not empty */ while (node1->data == node2->data && node1 != NULL && node2 !=NULL) { node1 = node1->next; node2 = node2->next; }; if(node1 != NULL) /* Nodes node1, node2 are not equal */ return 0; } return 1; }
6-46 Ο ΑΤΔ ΣΤΟΙΒΑ Οι κατασκευές που έχουμε δει μέχρι τώρα (διανύσματα, διασυνδεμένες λίστες με τις παραλλαγές τους, και ουρές) αποτελούν τη βάση για ακόμη πιο πολύπλοκες κατασκευές. Μία τέτοια κατασκευή είναι η στοίβα. Η στοίβα είναι και αυτή μία «λίστα» αντικειμένων, μόνο που έχει διαφορετική συμπεριφορά (δηλαδή έγκυρες πράξεις). Ο ΑΤΔ Στοίβα, είναι πολύ χρήσιμος για εφαρμογές στην επιστήμη υπολογιστών (computer science) Η συμπεριφορά το ΑΤΔ Στοίβα περιγράφεται ως εξής: –Τα νέα στοιχεία εισάγονται στην αρχή (ή καλύτερα) πάνω από τα ήδη υπάρχοντα στοιχεία. –Το στοιχείο που μπορεί να εξαχθεί από τη Στοίβα είναι το πάνω-πάνω στοιχείο (δηλαδή το πρώτο στοιχείο, αυτό που εισήχθη τελευταίο σε σειρά). –Επίσης μπορούμε να ελέγξουμε ένα η Στοίβα είναι άδεια, και πιο είναι το πάνω-πάνω στοιχείο της
6-47 Ο ΑΤΔ ΣΤΟΙΒΑ Εάν θεωρήσουμε την έννοια της Στοίβας αντικειμένων σαν ένα Αφηρημένο Τύπο Δεδομένων τότε αυτός θα έχει τις παρακάτω λειτουργίες (έγκυρες πράξεις) 1.Δημιουργία: Δημιουργεί μία κενή στοίβα 2.Κενή: Ελέγχει εάν μία στοίβα είναι κενή 3.Εισαγωγή : Δέχεται μία θέση στοίβα και εισάγει ένα στοιχείο πάνω από όλα τα άλλα. Η λειτουργία αυτή αναφέρεται στη βιβλιογραφία σαν push 4.Διαγραφή: Διαγράφει το πάνω-πάνω στοιχείο από τη στοίβα. Η λειτουργία αυτή αναφέρεται στη βιβλιογραφία σαν pop 5.Έλεγχος πρώτου στοιχείου: Δέχεται μια στοίβα, και επιστρέφει το στοιχείο που βρίσκεται πάνω-πάνω χωρίς να το διαγράφει από τη λίστα. Η λειτουργία αυτή αναφέρεται στη βιβλιογραφία σαν top
6-48 Ο ΑΤΔ ΣΤΟΙΒΑ Χαρακτηριστικά: –Μέγεθος Έγγυρες Πράξεις –Δημιουργία() : Στοίβα –Είναι_Κενή?(Στοίβα): λογικό –Push(Στοίβα, στοιχείο) : Στοίβα –Pop(Στοίβα) : στοιχείο –Top(Ουρά) : στοιχείο
6-49 ΥΛΟΠΟΙΗΣΗ ΤΟΥ ΑΤΔ ΣΤΟΙΒΑ Ο ΑΤΔ Στοίβα, μπορεί να υλοποιηθεί είτε με τη χρήση διανυσμάτων, ή με τη χρήση διασυνδεμένων λιστών. Εάν η υλοποίηση βασίζεται στη χρήση διανύσματος, τότε: 1.Πρέπει να γνωρίζουμε το μέγιστο επιτρεπτό μέγεθος της Στοίβας 2.Μπορούμε να θεωρήσουμε ότι το πάνω-πάνω στοιχείο είναι το πρώτο στοιχείο του διανύσματος, οπότε σε αυτή τη περίπτωση κάθε φορά που εισάγουμε ένα νέο στοιχείο θα πρέπει να μετατοπίζουμε τα στοιχεία του διανύσματος μία θέση δεξιά. Άλλες παραλλαγές της υλοποίησης με διάνυσμα είναι πιθανές (π.χ. Το πάνω στοιχείο να εισάγεται προς το τέλος του διανύσματος, οπότε πρέπει να γνωρίζουμε το τρέχον μέγεθος της στοίβας). 3.Εναλλακτικά μπορούμε να χρησιμοποιήσουμε διασυνδεμένες λίστες
6-50 ΥΛΟΠΟΙΗΣΗ ΤΗΣ ΣΤΟΙΒAΣ ΜΕ ΔΙΑNΥΣΜΑ #define length 100; int top = -1; void push(int stack[], int data) { int data; if(top+1==length) { printf("stack overflow\n"); exit(1); } top++; stack[top]=data; } int pop(int stack[]) { int value; int value; if(top==-1) { if(top==-1) { puts("stack is underflow"); puts("stack is underflow"); exit(1); exit(1); } value=stack[top]; value=stack[top]; top--; top--; return(value); return(value);} void main() { int stack[length]; int stack[length]; push(stack, 10); push(stack,20); pop(stack); }
6-51 ΥΛΟΠΟΙΗΣΗ ΤΗΣ ΣΤΟΙΒAΣ ΜΕ ΔΙΑΣΥΝΔΕΟΜΕΝΗ ΛΙΣΤΑ struct node { int data; struct node *next; }; typedef struct node nodeType; struct stack { nodeType *theStack; }; typedef struct stack stackType; StackType *aStack =(stackType *) malloc(sizeof(stackType)); aStack->theStack = NULL;
6-52 ΥΛΟΠΟΙΗΣΗ ΤΗΣ ΣΤΟΙΒAΣ ΜΕ ΔΙΑΣΥΝΔΕΟΜΕΝΗ ΛΙΣΤΑ void push(stackType *aStack, int data) { nodeType *t, *temp; int data; t=(nodeType *) malloc(sizeof(nodeType)); t->data=data; if(aStack->theStack == NULL) aStack->theStack=t; else { temp = aStack->theStack; t->next = temp; aStack->theStack=t; } int pop(stackType *aStack) { int value; int value; nodeType *t, *temp; nodeType *t, *temp; if(aStack->theStack==NULL) if(aStack->theStack==NULL) { puts("stack is underflow"); puts("stack is underflow"); exit(1); exit(1); } t= aStack->theStack; t= aStack->theStack; temp = t->next; temp = t->next; value=t->data; value=t->data; aStack->theStack = temp; aStack->theStack = temp; free(t); free(t); return(value); return(value);}
6-53 ΑΝΤΙΣΤΡΟΦΗ ΣΤΟΙΒΑΣ #define length 100; int top1 = -1; /* size of first stack */ Int top2 = -1; /*size of second stack */ void push(int stack[], int data, int *top) { int data; if(*top+1==length) { printf("stack overflow\n"); exit(1); } *top++; stack[*top]=data; } int pop(int stack[], int * top) { int value; int value; if(*top==-1) { if(*top==-1) { puts("stack is underflow"); puts("stack is underflow"); exit(1); exit(1); } value=stack[*top]; value=stack[*top]; *top--; *top--; return(value); return(value);} void reverse (stack1, stack2) { int size = top1; int data; for(int i=0; i<size; i++) { for(int i=0; i<size; i++) { data = pop(stack1, &top1); data = pop(stack1, &top1); push(stack2,data, &top2); push(stack2,data, &top2); } }
6-54 ΜΕΤΡΩΝΤΑΣ ΤΟ ΧΡΟΝΟ #include main() { int i; time_t t1, t2; (void) time(&t1); for (i=1;i<=300;i++) printf(``%d %d %d n'',i, i*i, i*i*i); (void) time(&t2); printf(`` n Time to do 300 squares and cubes= %d seconds n'', (int) t2-t1); }