Οικονοµικό Πανεπιστήµιο Αθηνών Τµήµα ιοικητικής Επιστήµης & Τεχνολογίας ΠΛΗΡΟΦΟΡΙΑΚΑ & ΤΗΛΕΠΙΚΟΙΝΩΝΙΑΚΑ ΣΥΣΤΗΜΑΤΑ Κεφάλαιο 9 οµές εδοµένων σε C Γιώργος Γιαγλής
Περίληψη Κεφαλαίου 9 οµές εδοµένων υναµικές οµές εδοµένων Λίστες ιάσχιση λίστας, Αναζήτηση στοιχείου σε λίστα, Εισαγωγή στοιχείου σε λίστα, ιαγραφή στοιχείου από λίστα, Άλλες µορφές λιστών Στοίβες Εισαγωγή στοιχείου σε στοίβα, Εξαγωγή στοιχείου από στοίβα, Υλοποίηση στοίβας µε πίνακα Ουρές Εισαγωγή στοιχείου σε ουρά, Εξαγωγή στοιχείου από ουρά, Υλοποίηση ουράς µε πίνακα ένδρα και Γράφοι Παράσταση δένδρων στη C, ιάσχιση δένδρου, υαδικά δένδρα αναζήτησης 2
υναµικές οµές εδοµένων Ως τώρα έχουµε συναντήσει δοµές δεδοµένων στις οποίες προκαθορίζεται το µέγεθος και πλήθος των στοιχείων- µεταβλητών Αποτέλεσµα Περιορισµένη χωρητικότητα των οµών εδοµένων, θα πρέπει το πρόγραµµα να ξαναγραφτεί στο µέλλον Υπερβολική και άσκοπη κατάληψη µνήµης από κενό πίνακα Αντιµετώπιση: υναµικές οµές εδοµένων Το µέγεθος τους αυξοµειώνεται ανάλογα µε τις ανάγκες, έτσι ώστε να χρησιµοποιεί πάντα ακριβώς το χώρο που τους είναι απαραίτητος (π.χ. ένας πίνακας για φοιτητές του ΟΠΑ, που το µέγεθος του να αυξάνεται κάθε φορά που ένας νέος φοιτητής εισάγεται στη βάση και να µειώνεται κάθε φορά που κάποιος διαγράφεται από αυτή ) Πώς; Με χρήση εικτών, επιτρέπουν την έµµεση αναφορά στη µνήµη του υπολογιστή Εφαρµογές Λίστες Στοίβες Ουρές
Λίστες Απλά Συνδεδεµένες υναµικές Λίστες Τι είναι; Μια συνεχόµενη αναπαράσταση στοιχείων( κόµβοι της λίστας) Κάθε κόµβος συνδέεται µε τον επόµενο µέσω µιας µεταβλητή δείκτη Παράδειγµα µιας απλής λίστας: 1 2 3 4 Έχουµε 4 κόµβους Κάθε κόµβος αποτελείται από 2 µέρη: στο πρώτο αποθηκεύεται η πληροφορία που µας ενδιαφέρει το δεύτερο µέρος είναι ένας δείκτης στον επόµενο κόµβο της λίστας ΠΑΡΑΤΗΡΗΣΕΙΣ: η υλοποίηση ενός κόµβου λίστας στη C θα γίνεται µε µια δοµή (struct) αρχή της λίστας ορίζεται από µια µεταβλητή δείκτη που «δείχνει» στον πρώτο κόµβο ο δείκτης στον τελευταίο κόµβο της λίστας έχει µια ειδική τιµή (ονοµάζεται nil ή null) που ορίζει ότι ο δείκτης αυτός δε δείχνει πουθενά
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 1: Αρχική ήλωση Λίστας Ορίζουµε τον τύπο των κόµβων της Ορίζουµε µια µεταβλητή δείκτη σε έναν τέτοιο κόµβο struct Komvos { int Plhroforia; struct Komvos *next; }; struct Komvos *Lista; Κάθε κόµβου αποτελείται από ένα ακέραιο αριθµό Plhroforia και ένα δείκτη ο οποίος ονοµάζεται next και δείχνει σε µια δοµή τύπου Komvos Σχηµατικά: Βήµα 1: Αρχική ήλωση Λίστας Lista?
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 2: ηµιουργία Πρώτου κόµβου Χρήση συνάρτησης malloc: επιτρέπει τη δυναµική δέσµευση χώρου µνήµης για χρήση από κάποια µεταβλητή Μεταβλητή είκτη = (ΤύποςΚόµβου *) malloc(sizeof(τύπ οςκόµβου)); Σχηµατικά: Όρισµα το µέγεθος της δοµής στην οποία θα δηµιουργήσει ένα δείκτη [sizeof(τύποςκόµβου)] Επιστρέφει ένα δείκτη σε µια τέτοια δοµή, (ΤύποςΚόµβου *) της παραπάνω δήλωσης. Ο δείκτης αυτός εκχωρείται σε κάποια Μεταβλητή είκτη, η οποία έτσι «δείχνει» σε µια «νέα» περιοχή της µνήµης που έχει δεσµευτεί για αυτό το σκοπό. Βήµα 2: ηµιουργία Πρώτου κόµβου Μεταβλητή είκτη??
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 2: ηµιουργία Πρώτου κόµβου Χρήση συνάρτησης malloc: επιτρέπει τη δυναµική δέσµευση χώρου µνήµης για χρήση από κάποια µεταβλητή Μεταβλητή είκτη = (ΤύποςΚόµβου *) malloc(sizeof(τύπ οςκόµβου)); Όρισµα το µέγεθος της δοµής στην οποία θα δηµιουργήσει ένα δείκτη [sizeof(τύποςκόµβου)] Επιστρέφει ένα δείκτη σε µια τέτοια δοµή, (ΤύποςΚόµβου *) της παραπάνω δήλωσης. Ο δείκτης αυτός εκχωρείται σε κάποια Μεταβλητή είκτη, η οποία έτσι «δείχνει» σε µια «νέα» περιοχή της µνήµης που έχει δεσµευτεί για αυτό το σκοπό. Σχηµατικά: Βήµα 2: ηµιουργία Πρώτου κόµβου Μεταβλητή είκτη??
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 2: ηµιουργία Πρώτου κόµβου ΠΑΡΑΤΗΡΗΣΕΙΣ: η Μεταβλητή είκτη έχει αποκτήσει τιµή και δείχνει σε ένα νέο κόµβο. ηλαδή δεσµεύεται για πρώτη φορά στη µνήµη, όχι στη δήλωση, αλλά στη διάρκεια εκτέλεσης του προγράµµατος Τα περιεχόµενα του κόµβου είναι ακόµα αόριστα, αφού δεν έχει δοθεί κάποια αρχική τιµή σε αυτά Μεταβλητή του κόµβου αυτού δεν έχει κάποιο όνοµα. Ο µόνος τρόπος προσπέλασης της είναι µέσω κάποιου δείκτη που δείχνει σε αυτή (έµµεση αναφορά µνήµης)
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 3: ηµιουργία Κόµβου στον οποίο θα δείχνει η Lista Χρήση συνάρτησης malloc Lista = (struct Komvos *) malloc(sizeof(struct Komvos)); εσµεύεται στη µνήµη ελεύθερος χώρος ίσο µε το χώρο που απαιτείται για µια µεταβλητή δοµής τύπου Komvos ίνεται τιµή στη µεταβλητή Lista, η διεύθυνση του πρώτου byte αυτού του νέου χώρου µνήµης Σχηµατικά: Βήµα 3: ηµιουργία Κόµβου στον οποίο θα δείχνει η Lista Lista??? ΠΑΡΑΤΗΡΗΣΗ: Τα περιεχόµενα του κόµβου που δηµιουργήθηκε έχουν αόριστη τιµή
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 4: Απόδοση τιµών στα περιεχόµενα του νέου κόµβου Χρήση τελεστή -> Μεταβλητή είκτη -> ΌνοµαΠεδίου Lista -> Plhroforia = 1; Lista -> next = NULL; Απόδοση τιµών στα πεδία Plhroforia και next του κόµβου που δηµιουργήσαµε Σχηµατικά: Βήµα 4: Απόδοση τιµών στα περιεχόµενα του νέου κόµβου Lista 1
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 5: ηµιουργία επόµενου κόµβου Α τρόπος Lista = (struct Komvos *) malloc(sizeof(struct Komvos)); Lista -> Plhroforia = 2; Lista -> next = NULL; Σχηµατικά: ηµιουργείται νέος κόµβος, η εντολή Lista δείχνει σ αυτόν. Απόδοση τιµών στον κόµβο Βήµα 5: ηµιουργία επόµενου κόµβου (α τρόπος) Lista 1 2 Μειονέκτηµα εν µπορούµε πια να προσπελάσουµε τον πρώτο κόµβο Προτεινόµενη Λύση Να αποδώσουµε τιµή στη µεταβλητή Lista -> next του πρώτου κόµβου (β τρόπος)
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 5: ηµιουργία επόµενου κόµβου Β τρόπος Lista -> next = (struct Komvos *) malloc(sizeof(struct Komvos)); Lista -> next -> Plhroforia = 2; Lista -> next -> next = NULL; Απόδοση τιµής στη µεταβλητή Lista -> next του πρώτου κόµβου Σχηµατικά: Βήµα 5: ηµιουργία επόµενου κόµβου (β τρόπος) Lista 1 2 Μειονέκτηµα εν µπορεί να χρησιµοποιηθεί αποτελεσµατικά για τη δηµιουργία πολλών κόµβων (πχ αν είχαµε 100 κόµβους;) εν µπορεί να εκµεταλλευτεί τη δύναµη των εντολών επανάληψης Προτεινόµενη Λύση Χρήση µιας δεύτερης µεταβλητής δείκτη, έστω temp, που να δείχνει πάντα στον τελευταίο κόµβο της λίστας µας
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 5: ηµιουργία επόµενου κόµβου Γ τρόπος Komvos *temp; temp = (struct Komvos *) malloc(sizeof(struct Komvos)); temp -> Plhroforia = 2; temp -> next = NULL; Lista -> next = temp; 1) Ορίζει µια µεταβλητή δείκτη σε κόµβο µε το όνοµα temp 2) ηµιουργεί ένα νέο κόµβο στον οποίο θα δείχνει η temp 3)Αποδίδει περιεχόµενα κόµβο στον οποίο δείχνει η temp 4) Συνδέει τους δυο κόµβους µέσω απόδοσης τιµής στη µεταβλητή Lista -> next έτσι ώστε αυτή να δείχνει στον ίδιο κόµβο που δείχνει και η temp Προτεινόµενος τρόπος: γ τρόπος δηµιουργεί µια λίστα µε δυο κόµβους µέσω της µεταβλητής temp µπορούµε πια να ορίσουµε και τους υπόλοιπους κόµβους µε µια εντολή επανάληψης χωρίς να χρειαστεί να ξαναπειράξουµε την αρχική µεταβλητή Lista
Λίστες, ηµιουργία Λίστας ηµιουργία Λίστας Βήµα 5: ηµιουργία επόµενου κόµβου (γ τρόπος) Σχηµατικά Lista 1 Lista 1 temp? temp?? Lista 1 Lista 1 temp 2 temp 2 14
Λίστες, ιάσχιση Λίστας Η Λίστα είναι µια µορφή δυναµικής δοµής δεδοµένων Η δυναµική διαχείριση µνήµης δηµιουργεί δυσκολίες στον τρόπο άµεσης αναφοράς στα περιεχόµενα της λίστας. ηλαδή µόνο ο πρώτος κόµβος µπορεί να προσπελαστεί άµεσα. Για να προσπελαστεί ο κόµβος που επιθυµούµε, πρέπει να προσπελαστούν και όλοι οι προηγούµενοι του 15
Λίστες, ιάσχιση Λίστας Αλγόριθµος ιάσχισης Λίστας 1. Ορίζουµε µια βοηθητική µεταβλητή δείκτη, έστω temp(απαραίτητη για να διασχίσουµε τη λίστα χωρίς να αλλάξουµε τα περιεχόµενα της µεταβλητής Lista) 2. Μετακινούµαστε από κόµβο σε κόµβο µέσω της µεταβλητής temp->next, η οποία δείχνει πάντα στον επόµενο κόµβο της λίστας 3. Η διάσχιση τελειώνει όταν η µεταβλητή temp->next έχει τιµή NULL(τέλος λίστας) ή όταν βρεθεί το ζητούµενο στοιχείο ΠΡΟΣΟΧΗ: Οριακές περιπτώσεις Άδεια δοµή (δείτε το παράδειγµα που ακολουθεί) Γεµάτη οµή Η δραστηριότητα που θέλουµε να επιτελέσουµε πρέπει να γίνει στον πρώτο κόµβο (δείτε το παράδειγµα δηµιουργίας της λίστας, όπου ο πρώτος κόµβος δηµιουργείται διαφορετικά από τους υπόλοιπους) Η δραστηριότητα που θέλουµε να επιτελέσουµε πρέπει να γίνει στον τελευταίο κόµβο 16
Λίστες, ιάσχιση Λίστας Αλγόριθµος ιάσχισης Λίστας temp = Lista; /* αρχικοποίηση µεταβλητής temp */ if (temp == NULL) /* αν η λίστα είναι κενή */ printf( Κενή Λίστα! ) else do printf( %d, temp -> Plhroforia); /* εκτύπωση τιµής κάθε κόµβου */ temp = temp -> next; /* µετακίνηση στον επόµενο κόµβο */ while (temp!= NULL); /* όσο υπάρχουν ακόµα επόµενοι κόµβοι */ temp = Lista; /* αρχικοποίηση µεταβλητής temp */ if (temp == NULL) /* αν η λίστα είναι κενή */ printf( Κενή Λίστα! ) else do printf( %d, temp -> Plhroforia); /* εκτύπωση τιµής κάθε κόµβου */ temp = temp -> next; /* µετακίνηση στον επόµενο κόµβο */ while (temp!= NULL); /* όσο υπάρχουν ακόµα επόµενοι κόµβοι */ 17
Λίστες, ιάσχιση Λίστας Εναλλακτικός Αλγόριθµος ιάσχισης Λίστας temp = Lista; /* αρχικοποίηση µεταβλητής temp */ while (temp!= NULL) /* όσο η λίστα δεν είναι κενή */ {printf( %d, temp -> Plhroforia); /* εκτύπωση τιµής κάθε κόµβου */ temp = temp -> next; /* µετακίνηση στον επόµενο κόµβο */ } ΜΕΙΟΝΕΚΤΗΜΑ εν τυπώνει τίποτα σε περίπτωση που η λίστα είναι κενή 18
Λίστες, Αναζήτηση Στοιχείου σε Λίστα Στις Λίστες µπορούµε να εφαρµόσουµε ΜΟΝΟ Γραµµική Αναζήτηση Αλγόριθµος παρόµοιος µε αλγόριθµο διάσχισης Παράδειγµα αλγορίθµου Αναζήτησης σε µη ταξινοµηµένη λίστα Αναζήτηση στοιχείου µε τιµή Stoxos (βοηθητική ακέραια µεταβλητή Vrethike (true) αν το στοιχείο βρεθεί και (false) διαφορετικά) temp = Lista; /* αρχικοποίηση µεταβλητής temp */ Vrethike = 0; /* αρχικοποίηση µεταβλητής: το στοιχείο δεν έχει βρεθεί */ while ((temp!=null) && (!Vrethike)) /* όσο δεν τέλειωσε η λίστα και δεν έχει βρεθεί */ if (temp -> Plhroforia == Stoxos) /* αν το στοιχείο βρεθεί */ Vrethike = 1; if (Vrethike) /* αν η επανάληψη τελειώσει επειδή το στοιχείο βρέθηκε */ printf( Το στοιχείο υπάρχει στη λίστα ) else /* αν η επανάληψη τελειώσει επειδή η λίστα εξαντλήθηκε */ printf( Το στοιχείο δεν υπάρχει στη λίστα ); 19
Λίστες, Αναζήτηση Στοιχείου σε Λίστα Παράδειγµα αλγορίθµου Αναζήτησης σε ταξινοµηµένη λίστα Αναζήτηση στοιχείου µε τιµή Stoxos Ο Αλγόριθµος τερµατίζει αν βρεθεί στοιχείο µε µεγαλύτερη τιµή από το ζητούµενο temp = Lista; /* αρχικοποίηση µεταβλητής temp */ Vrethike = 0; /* αρχικοποίηση µεταβλητής: το στοιχείο δεν έχει βρεθεί */ while ((temp!=null) && (!Vrethike)) /* όσο δεν τέλειωσε η λίστα και δεν έχει βρεθεί */ if (temp -> Plhroforia == Stoxos) /* αν το στοιχείο βρεθεί */ Vrethike = 1 else if (temp -> Plhroforia > Stoxos) /* αν ξεπεράσαµε την τιµήστόχο */ temp = NULL; /* αναγκάζουµε την επανάληψη να τελειώσει */ if (Vrethike) /* αν η επανάληψη τελειώσει επειδή το στοιχείο βρέθηκε */ printf( Το στοιχείο υπάρχει στη λίστα ) else /* αν η επανάληψη τελειώσει επειδή η λίστα εξαντλήθηκε */ printf( Το στοιχείο δεν υπάρχει στη λίστα ); 20
Λίστες, Εισαγωγή στοιχείου σε λίστα Αλγόριθµος εισαγωγής στοιχείου Στην αρχή µη ταξινοµηµένης λίστας Στο τέλος µη ταξινοµηµένης λίστας Σε ταξινοµηµένη λίστα (παραµένει ταξινοµηµένη και µετά την εισαγωγή) 21
Λίστες, Εισαγωγή στοιχείου σε λίστα Εισαγωγή στοιχείου στην αρχή µη ταξινοµηµένης Λίστας - Παράδειγµα 1. ηµιουργία κόµβου µε τη συνάρτηση malloc 2. Ο δείκτης next του κόµβου αυτού να δείχνει εκεί που δείχνει αρχικά ο δείκτης Lista 3. Ο δείκτης Lista δείχνει στο νέο κόµβο 4. Ονοµάζουµε το νέο κόµβο Neos και έστω x το στοιχείο (πληροφορία) που θέλουµε να εισάγουµε Neos = (struct Komvos *) malloc(sizeof(struct Komvos)); /* δηµιουργία κόµβου */ Neos -> Plhroforia = x; /* απόδοση τιµής στο νέο κόµβο */ Neos -> next = Lista; /* ο νέος κόµβος δείχνει στην προηγούµενη αρχή της λίστας */ Lista = Neos; /* η λίστα ξεκινάει πια από το νέο κόµβο */ 22
Λίστες, Εισαγωγή στοιχείου σε λίστα Εισαγωγή στοιχείου στο τέλος µη ταξινοµηµένης Λίστας - Παράδειγµα 1. ηµιουργία κόµβου µε τη συνάρτηση malloc 2. ιάσχιση της υπάρχουσας λίστας 3. Ο δείκτης next του τελευταίου κόµβου να δείχνει στο νέο κόµβο Neos = (struct Komvos *) malloc(sizeof(struct Komvos)); /* δηµιουργία κόµβου */ Neos -> Plhroforia = x; /* απόδοση τιµής στο νέο κόµβο */ Neos -> next = NULL; /* ο νέος κόµβος δε δείχνει πουθενά */ if (Lista == NULL) /* αν η λίστα ήταν αρχικά άδεια */ Lista = Neos; /* η λίστα θα δείχνει στο νέο κόµβο */ else {temp = Lista; /* βοηθητική µεταβλητή διάσχισης της λίστας */ while (temp -> next!= NULL) /* µέχρι να τελειώσει η λίστα */ temp = temp -> next; /* προχωράµε στον επόµενο κόµβο */ temp -> next = Neos; /* ο τελευταίος κόµβος θα δείχνει στο νέο */ } 23
Λίστες, Εισαγωγή στοιχείου σε λίστα Εισαγωγή στοιχείου σε ταξινοµηµένη λίστα- Παράδειγµα 1. ηµιουργία κόµβου µε τη συνάρτηση malloc 2. ιάσχιση της υπάρχουσας λίστας µέχρι να βρούµε τη σωστή θέση εισαγωγής του νέου κόµβου 3. Ενηµέρωση δυο δεικτών: το δείκτη next του προηγούµενου κόµβου της λίστας να δείχνει στο νέο κόµβο και το δείκτη next του νέου κόµβου να δείχνει στον επόµενο 24
Λίστες, Εισαγωγή στοιχείου σε λίστα Εισαγωγή στοιχείου σε ταξινοµηµένη λίστα- Παράδειγµα Neos = (struct Komvos *) malloc(sizeof(struct Komvos)); /* δηµιουργία κόµβου */ Neos -> Plhroforia = x; /* απόδοση τιµής στο νέο κόµβο */ if (Lista == NULL) /* αν η λίστα ήταν αρχικά άδεια */ {Lista = Neos; /* η λίστα θα δείχνει στο νέο κόµβο */ Neos -> next = NULL; /* ο νέος κόµβος δε θα δείχνει πουθενά */ else {temp = Lista; /* βοηθητική µεταβλητή διάσχισης της λίστας */ previous = NULL; /* βοηθητική µεταβλητή που δείχνει τον προηγούµενο κόµβο */ while ((temp!=null) && (temp -> Plhroforia < x)) /* µέχρι να τελειώσει η λίστα ή να βρεθεί η σωστή θέση */ {previous = temp; /* προχώρησε τον προηγούµενο κόµβο µια θέση */ temp = temp -> next; /* προχώρησε τον τρέχοντα κόµβο µια θέση */ } previous -> next = Neos; /* ο δείκτης next του προηγούµενου κόµβου δείχνει το νέο */ Neos -> next = temp; /* ο δείκτης next του νέου κόµβου δείχνει τον επόµενο */ } 25
Λίστες, ιαγραφή στοιχείου από λίστα ιαγραφή = Απελευθέρωση µνήµης Γίνεται µε χρήση της συνάρτησης free (ουσιαστικά αντίθετη της malloc) free(μεταβλητή είκτη); Αλγόριθµος ιαγραφής 1. διασχίζει πρώτα τη λίστα µέχρι να βρει τον κόµβο προς διαγραφή 2. ενηµερώνει το δείκτη next του προηγούµενου κόµβου ώστε να δείχνει προς τον επόµενο 3. απελευθερώνει το χώρο µνήµης του διαγραφέντος κόµβου ΠΡΙΝ: Lista 1 2 3 ΜΕΤΑ: Lista 1 2 3 26
Λίστες, ιαγραφή στοιχείου από λίστα Αλγόριθµος ιαγραφής - Παράδειγµα if (Lista == NULL) /* αν η λίστα είναι άδεια */ printf( εν µπορεί να γίνει διαγραφή ); else if (Lista -> Plhroforia == x) /* αν το ζητούµενο στοιχείο είναι το πρώτο */ {temp = Lista; /* ο τρέχων κόµβος είναι ο πρώτος */ Lista = Lista -> next; /* η λίστα ξεκινάει από τον επόµενο κόµβο */ free(temp); /* απελευθερώνεται η µνήµη του τρέχοντος κόµβου */ } else {temp = Lista; /* βοηθητική µεταβλητή διάσχισης της λίστας */ previous = NULL; /* βοηθητική µεταβλητή που δείχνει τον προηγούµενο κόµβο */ while ((temp!=null) && (temp -> Plhroforia!= x)) /* µέχρι να τελειώσει η λίστα ή να βρεθεί ο κόµβος */ {previous = temp; /* προχώρησε τον προηγούµενο κόµβο µια θέση */ temp = temp -> next; /* προχώρησε τον τρέχοντα κόµβο µια θέση */ } if (temp!=null) /* αν το στοιχείο βρέθηκε */ {previous->next = temp->next; /* ο δείκτης του προηγούµενου δείχνει το επόµενο */ free(temp); /* απελευθερώνεται η µνήµη του τρέχοντος κόµβου */ } else /* αν το στοιχείο προς διαγραφή δεν υπάρχει */ printf( Το στοιχείο δεν υπάρχει στη λίστα ); 27 }
Λίστες, Άλλες Μορφές Λίστας Απλά συνδεδεµένες Λίστες: κάθε κόµβος συνδέεται µόνο µε τον επόµενο του (όσες είδαµε έως τώρα) ιπλά συνδεδεµένες Λίστες: κάθε κόµβος αποτελείται από τρία στοιχεία: την πληροφορία, ένα δείκτη στον επόµενο κόµβο και ένα δείκτη στον προηγούµενο κόµβο DiplhLista 1 2 3 Κυκλικές Λίστες: ίδιες µε απλά συνδεδεµένες ΑΛΛΑ ο δείκτης επόµενου του τελευταίου κόµβου δείχνει πίσω στον πρώτο κόµβο της λίστας Kyklikh Lista 1 2 3 28
Στοίβες Στοίβα (stack): ειδική µορφή λίστας, τα στοιχεία εισάγονται και εξάγονται µόνο από το ένα άκρο της (κορυφή της στοίβας) Το τελευταίο στοιχείο που µπαίνει στη στοίβα βγαίνει πρώτο οµή LIFO, Last In First Out Παραδείγµατα στο φυσικό κόσµο: Μια στοίβα πιάτα Είσοδος αυτοκινήτων σε γκαράζ πλοίων 29
Στοίβες, Ορισµός Στοίβας Σχηµατική Αναπαράσταση Στοίβας ΑΝΑΠΑΡΑΣΤΑΣΗ Stoiva 3 2 1 ΕΙΣΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Stoiva 3 2 1 4 ΕΞΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Stoiva 3 2 1 4 30
Στοίβες, Ορισµός Στοίβας Ορισµός Στοίβας σε C /* ορισµός δοµής για τους κόµβους της στοίβας */ struct KomvosStoivas { int Plhroforia; struct KomvosStoivas *next; }; /* ορισµός µεταβλητής που θα δείχνει πάντα την κορυφή της στοίβας */ struct KomvosStoivas *Stoiva; /* απόδοση αρχικής τιµής στην (αρχικά άδεια) στοίβα */ Stoiva = NULL; 31
Στοίβες, Εισαγωγή στοιχείου σε Στοίβα Εισαγωγή στοιχείου σε στοίβα 1. Χρήση malloc για δηµιουργία ο νέου κόµβου µε την προς εισαγωγή πληροφορία 2. Απόδοση στο δείκτη next του κόµβου αυτού της τιµής του δείκτη της στοίβας 3. Αλλάγή της τιµή του δείκτη της στοίβας ώστε να δείχνει στο νέο κόµβο /* δηµιουργία κόµβου */ Neos = (struct KomvosStoivas *) malloc(sizeof(struct KomvosStoivas)); Neos -> Plhroforia = x; /* απόδοση τιµής στο νέο κόµβο */ Neos -> next = Stoiva; /* ο δείκτης next του νέου κόµβου δείχνει στην παλιά κορυφή */ Stoiva = Neos; /* η στοίβα τώρα ξεκινάει από το νέο κόµβο */ 32
Στοίβες, Εξαγωγή στοιχείου από Στοίβα Εξαγωγή στοιχείου από Λίστα 1. Ανάκληση η πληροφορίας του αρχικού κόµβου 2. Αλλαγή της τιµής του δείκτη της στοίβας ώστε να δείχνει εκεί που έδειχνε η παλιά κορυφή της 3. Απελευθέρωση της µνήµης που καταλάµβανε η παλιά κορυφή Οριακή περίπτωση: άδεια στοίβα if (Stoiva == NULL) printf( Στοίβα άδεια. εν µπορεί να γίνει διαγραφή ); else {temp = Stoiva; /* προσωρινός κόµβος που θα απελευθερωθεί */ x = Stoiva -> Plhroforia; /* ανάκληση τιµής που θα εξαχθεί */ Stoiva = Stoiva -> next; /* αλλαγή τιµής στην κορυφή της στοίβας */ free(temp); /* απελευθέρωση µνήµης του προσωρινού κόµβου */ } 33
Στοίβες, Υλοποίηση Στοίβας µε Πίνακα Η Στοίβα µπορεί να υλοποιηθεί ΚΑΙ µε στατικό τρόπο, δηλαδή µε Πίνακα Τότε η στοίβα αποτελείται από δυο µεταβλητές ένα πίνακα Ν θέσεων στον οποίο αποθηκεύονται τα στοιχεία της στοίβας µια ακέραια µεταβλητή η οποία αναπαριστά την κορυφή της στοίβας (δείχνει πόσα στοιχεία έχει η στοίβα) Υλοποίηση Στοίβας µε Πίνακα µπορεί να γίνει µε πολλούς τρόπους Ένας από αυτούς: η κορυφή να δείχνει πάντα στην πρώτη ελεύθερη θέση (δηλαδή µετά το τελευταίο στοιχείο) Πλεονέκτηµα Στατικής Στοίβας Ευκολότερη υλοποίηση περισσότερων πράξεων Μειονέκτηµα Στατικής Στοίβας Μειονέκτηµα στατικών δοµών, σταθερό µέγεθος 34
Στοίβες, Υλοποίηση Στοίβας µε Πίνακα Σχηµατική Απεικόνιση Στατικής Λίστας ΑΝΑΠΑΡΑΣΤΑΣΗ Stoiva 1 2 3 top ΕΙΣΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Stoiva 1 2 3 4 top ΕΞΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Stoiva 1 2 3 4 top 35
Στοίβες, Υλοποίηση Στοίβας µε Πίνακα Ορισµός Στατικής Στοίβας σε C /* ορισµός δοµής που οµαδοποιεί τον πίνακα και το δείκτη κορυφής της στοίβας */ struct StatikiStoiva { int Plhroforia[N]; int top; }; /* ορισµός µεταβλητής για τη στοίβα */ struct StatikiStoiva Stoiva; /* απόδοση αρχικής τιµής στην (αρχικά άδεια) στοίβα */ Stoiva.top = 0; ΠΡΟΣΟΧΗ Η οµαδοποίηση του πίνακα Plhroforia και του δείκτη top σε µια δοµή είναι προαιρετική Χρήσιµη όταν σε ένα πρόγραµµα υπάρχουν περισσότερες από µια στοίβες, ιευκολύνει τον ορισµό και τη διαχείριση τους µέσω δοµών. είκτης top µε τιµή 0 δηλώνει ότι η στοίβα αρχικά είναι κενή από στοιχεία 36
Στοίβες, Υλοποίηση Στοίβας µε Πίνακα Εισαγωγή Στοιχείου Αυξάνοντας το δείκτη top κατά ένα Εισάγοντας το νέο στοιχείο x στη θέση αυτή του πίνακα ΠΡΟΣΟΧΗ: Έλεγχος αν η Στοίβα είναι ήδη γεµάτη (δε χρειάζεται στην περίπτωση δυναµικής υλοποίησης) if (Stoiva.top == N) /* αν η στοίβα έχει ήδη Ν στοιχεία */ printf( Η στοίβα είναι γεµάτη. εν µπορεί να γίνει εισαγωγή ); else {Stoiva.Plhroforia[top]=x; /* προσέξτε ότι ο δείκτης top παίρνει τιµές από 0 ως Ν-1 */ Stoiva.top++; /* αύξηση δείκτη κορυφής */ } 37
Στοίβες, Υλοποίηση Στοίβας µε Πίνακα Εξαγωγή Στοιχείου Ανάκληση της πληροφορία που βρίσκεται στην κορυφή της στοίβας Μειώνοντας του δείκτη top κατά ένα ΠΡΟΣΟΧΗ: το στοιχείο στην πραγµατικότητα δε διαγράφεται από τον πίνακα, απλά µαρκάρεται η θέση του ως ελεύθερη αλλάζοντας την τιµή του δείκτη top Έλεγχος αν η Στοίβα είναι άδεια if (Stoiva.top == 0) /* αν η στοίβα δεν έχει στοιχεία */ printf( Η στοίβα είναι άδεια. εν µπορεί να γίνει εξαγωγή ); else {Stoiva.top--; /* µείωση δείκτη κορυφής */ x=stoiva.plhroforia[top]; /* προσέξτε ότι ο δείκτης top παίρνει τιµές από 0 ως Ν-1 */ } 38
Ουρές Ουρά (queue): ειδική µορφή λίστας, τα στοιχεία εισάγονται από το ένα άκρο και εξάγονται από το άλλο. Κάθε νέο στοιχείο εισάγεται στο τέλος ενώ κάθε στοιχείο εξέρχεται από την αρχή της ουράς οµή FIFO, Fast In First Out Παράδειγµα στο φυσικό κόσµο: Ουρά πελατών µε µια τράπεζα Συνήθως έχει τη µορφή µιας απλά συνδεδεµένης λίστας µε ένα επιπλέον δείκτη που δείχνει κάθε φορά στο τελευταίο στοιχείο της 39
Ουρές, Ορισµός Ουράς Σχηµατική Αναπαράσταση Ουράς Telos ΑΝΑΠΑΡΑΣΤΑΣΗ Arxh 1 2 3 Telos ΕΙΣΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Arxh 1 2 3 4 ΕΞΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Arxh Telos 1 2 3 4 40
Ουρές, Ορισµός Ουράς Ορισµός Ουράς σε C /* ορισµός δοµής για τους κόµβους της ουράς */ struct KomvosOuras { int Plhroforia; struct KomvosOuras *next; }; /* ορισµός µεταβλητών που θα δείχνουν στην αρχή και το τέλος της ουράς */ struct KomvosOuras *Arxh, *Telos; /* απόδοση αρχικών τιµών στην (αρχικά άδεια) ουρά */ Arxh = NULL; Telos = NULL; 41
Ουρές, Εισαγωγή στοιχείου σε Ουρά Εισαγωγή στοιχείου σε Ουρά Η εισαγωγή γίνεται πάντα µετά το δείκτη Telos 1. Χρήση malloc για δηµιουργία ο νέου κόµβου µε την προς εισαγωγή πληροφορία (ο οποίος δε θα δείχνει πουθενά) 2. Αλλαγή του δείκτη next του τελευταίου κόµβου ώστε να δείχνει στο νέο 3. Αλλαγή της τιµή του δείκτη Telos ώστε να δείχνει στο νέο κόµβο /* δηµιουργία κόµβου */ Neos = (struct KomvosOuras *) malloc(sizeof(struct KomvosOuras)); Neos -> Plhroforia = x; /* απόδοση τιµής στο νέο κόµβο */ Neos -> next = NULL; /* ο νέος κόµβος δε δείχνει πουθενά */ if (Telos!= NULL) /* αν η ουρά δεν είναι άδεια */ Telos -> next = Neos; /* ο προηγούµενος τελευταίος κόµβος δείχνει το νέο κόµβο */ Telos = Neos; /* ο δείκτης τέλους δείχνει το νέο κόµβο */ if (Arxh == NULL) /* αν η ουρά ήταν άδεια) Arxh = Neos; /* ο δείκτης αρχής δείχνει το νέο κόµβο */ 42
Ουρές, Εξαγωγή στοιχείου από Ουρές Εξαγωγή στοιχείου από Ουρά (γίνεται από την κορυφή της ουράς) 1. Ανάκληση η πληροφορίας του αρχικού κόµβου 2. Αλλαγή της τιµής του δείκτη αρχής ώστε να δείχνει εκεί που έδειχνε ο παλιός δείκτης αρχής 3. Απελευθέρωση της µνήµης που καταλάµβανε ο παλιός αρχικός κόµβος Οριακή περίπτωση: Η ουρά είναι άδεια ή αδειάζει µετά τη διαγραφή. Πρέπει να ενηµερωθεί ο δείκτης Τέλους if Arxh == NULL printf( Ουρά άδεια. εν µπορεί να γίνει διαγραφή ); else {temp = Arxh; /* προσωρινός κόµβος που θα απελευθερωθεί */ x = Arxh -> Plhroforia; /* ανάκληση τιµής που θα εξαχθεί */ Arxh = Arxh -> next; /* αλλαγή τιµής στην κορυφή της ουράς */ if (Arxh == NULL) /* αν η ουρά άδειασε µετά τη διαγραφή */ Telos = NULL; /* ενηµέρωση του δείκτη τέλους */ free(temp); /* απελευθέρωση µνήµης του προσωρινού κόµβου */ } 43
Ουρές, Υλοποίηση Ουράς µε Πίνακα Η Ουρά µπορεί να υλοποιηθεί ΚΑΙ µε στατικό τρόπο, δηλαδή µε Πίνακα Τότε η ουρά αποτελείται από τρεις µεταβλητές ένα πίνακα Ν θέσεων στον οποίο αποθηκεύονται τα στοιχεία της ουράς δυο ακέραιες µεταβλητές οι οποίες αναπαριστούν την αρχή και το τέλος της ουράς Υλοποίηση Ουράς µε Πίνακα µπορεί να γίνει µε πολλούς τρόπους Ένας από αυτούς: Ο δείκτης αρχής να δείχνει πάντα στο πρώτο στοιχείο και ο δείκτης τέλους στην πρώτη ελεύθερη θέση (δηλαδή µετά το τελευταίο στοιχείο) Πλεονέκτηµα Στατικής Στοίβας Ευκολότερη υλοποίηση περισσότερων πράξεων Μειονέκτηµα Στατικής Στοίβας Μειονέκτηµα στατικών δοµών, σταθερό µέγεθος 44
Ουρές, Υλοποίηση Ουράς µε Πίνακα Σχηµατική Απεικόνιση Στατικής Ουράς ΑΝΑΠΑΡΑΣΤΑΣΗ Oura 1 2 3 Arxh Telos ΕΙΣΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Oura 1 2 3 Arxh 4 Telos ΕΞΑΓΩΓΗ ΣΤΟΙΧΕΙΟΥ Oura 1 2 3 Arxh 4 Telos 45
Ουρές, Υλοποίηση Ουράς µε Πίνακα Ορισµός Στατικής Ουράς σε C /* ορισµός δοµής για τα στοιχεία της στατικής ουράς */ struct StatikiOura { int Plhroforia[N]; int Arxh; int Telos; }; /* ορισµός µεταβλητής για την ουρά */ struct StatikiOura Oura; /* απόδοση αρχικών τιµών στα στοιχεία της (αρχικά άδειας) ουράς */ Oura.Arxh = 0; Oura.Telos = 0; 46
Ουρές, Υλοποίηση Ουράς µε Πίνακα Εισαγωγή Στοιχείου Εισάγοντας το νέο στοιχείο x στη θέση του πίνακα που δείχνει ο δείκτης τέλους Αυξάνοντας το δείκτη Telos κατά ένα ΠΡΟΣΟΧΗ: Έλεγχος αν η Όυρά είναι ήδη γεµάτη Όταν ο δείκτης τέλους φτάσει στο τέλος του πίνακα, να ελέγχεται αν υπάρχουν άδειες θέσεις και να µετακινούνται όλα τα στοιχεία µπροστά (αλλιώς θα υπάρχουν άδειες θέσεις και δεν θα µπορούν να γίνουν εισαγωγές) if (Oura.Telos < N) /* αν το στοιχείο χωράει στο τέλος του πίνακα */ {Oura.Plhroforia[Telos]=x; /* το νέο στοιχείο εισάγεται στο τέλος */ Oura.Telos++; /* αύξηση δείκτη τέλους */ } else /* αν ο δείκτης τέλους έχει φτάσει στο τέλος του πίνακα */ if (Oura.Arxh == 0) /* αν ο δείκτης αρχής βρίσκεται στην αρχή */ printf( Η ουρά είναι γεµάτη. εν µπορεί να γίνει εισαγωγή ); else /* πρέπει να γίνει µετακίνηση στοιχείων */ {StoixeiaOuras = Telos - Arxh; /* πόσα στοιχεία πρέπει να µετακινηθούν */ for (i=arxh;i>0;i--) /* µετακίνηση */ for (k=1;k<=stoixeiaouras;k++) /* >> */ Oura.Plhroforia[i+k-2]=Oura.Plhroforia[i+k-1]; /* >> */ Arxh = 0; /* ενηµέρωση νέου δείκτη αρχής */ 47
Ουρές, Υλοποίηση Ουράς µε Πίνακα Εξαγωγή Στοιχείου Ανάκληση της πληροφορία που βρίσκεται στην αρχή της ουράς Αυξάνοντας το δείκτη Arxh κατά ένα ΠΡΟΣΟΧΗ: Έλεγχος αν η Στοίβα είναι άδεια ή αν αδειάσει µετά την εξαγωγή if (Oura.Telos == 0) /* αν η ουρά δεν έχει στοιχεία */ printf( Η ουρά είναι άδεια. εν µπορεί να γίνει εξαγωγή ); else {x=oura.plhroforia[arxh]; /* εξαγωγή του πρώτου στοιχείου */ Oura.Arxh++; /* αύξηση δείκτη αρχής */ if (Oura.Arxh == Oura.Telos) /* αν µετά την εξαγωγή η ουρά άδειασε */ {Oura.Arxh = 0; /* ενηµέρωση δεικτών */ Oura.Telos = 0; /* >> >> */ } } 48