ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ Μια δομή δεδομένων στην πληροφορική, συχνά αναπαριστά οντότητες του φυσικού κόσμου στον υπολογιστή. Για την αναπαράσταση αυτή, δημιουργούμε πρώτα ένα αφηρημένο μοντέλο στο οποίο προσδιορίζονται οι λειτουργίες ή οι πράξεις. Το μοντέλο αυτό ονομάζεται αφηρημένος τύπος δεδομένων. Η μεταφορά του αφηρημένου τύπου στον υπολογιστή (δηλαδή η υλοποίηση του μοντέλου αυτού) πραγματοποιείται με διάφορους τρόπους. Το τελικό αποτέλεσμα συνιστά τη δημιουργία μιας δομής δεδομένων στην οποία εισάγονται τα δεδομένα. Σε κάθε δομή δεδομένων, η επεξεργασία των στοιχείων γίνεται με βάση τις πράξεις που έχουν καθοριστεί από την αρχή, τότε δηλαδή που δημιουργήθηκε ο αντίστοιχος αφηρημένος τύπος δεδομένων. Οι δομές που θα μελετήσουμε στο κεφάλαιο αυτό είναι θεμελιώδεις και αποτελούν πρωταρχικό υλικό στη θεωρία των αλγορίθμων και του προγραμματισμού. Σε κάθε μία από αυτές περιγράφονται και υλοποιούνται οι σημαντικότερες από τις λειτουργίες τους, όπως είναι για παράδειγμα η εισαγωγή, η διαγραφή, η αναζήτηση, η ταξινόμηση, η εκτύπωση κ.α. Για τη δημιουργία μιας δομής δεδομένων χρησιμοποιούνται κυρίως δυο μέθοδοι. Η πρώτη μέθοδος χρησιμοποιεί τη δομή του πίνακα. Σύμφωνα με τα χαρακτηριστικά της δομής του πίνακα, τα στοιχεία εισάγονται και τοποθετούνται σε προκαθορισμένες διαδοχικές θέσεις. Συνεπώς δημιουργούνται στατικές δομές δεδομένων. Με τον όρο στατική δομή δεδομένων εννοούμε τη δομή το μέγεθος της οποίας καθορίζεται από την αρχή στο πρόγραμμα και όχι κατά τη διάρκεια της εκτέλεσης του. Η δεύτερη μέθοδος βασίζεται στη δημιουργία κόμβων με δυναμικό τρόπο, οι οποίοι συνδέονται μεταξύ τους. Αυτή η μέθοδος, δημιουργεί δυναμικές δομές δεδομένων. Με τον όρο δυναμική δομή δεδομένων εννοούμε μια δομή της οποίας το μέγεθος μεταβάλλεται ανάλογα με τα δεδομένα που εισάγονται ή διαγράφονται. Σε αυτό το σημείο αξίζει να σημειωθεί ότι τα δεδομένα δεν τοποθετούνται σε συνεχόμενες θέσεις μνήμης. Αναφέρουμε ως παραδείγματα τέτοιων δομών τη στοίβα, την ουρά και τα δένδρα. Η καταχώρηση των στοιχείων με τον τρόπο αυτό λέγεται και αλυσιδωτή καταχώρηση. Ανάλογα με το σχήμα της καταχώρησης που χρησιμοποιείται, ο προσδιορισμός της θέσης ενός στοιχείου ποικίλει. Στους πίνακες χρησιμοποιείται ένας αύξων αριθμός, ο οποίος ονομάζεται αριθμοδείκτης. Στις δυναμικές δομές χρησιμοποιούνται οι δείκτες (pointers) οι οποίοι αντιστοιχούν στις διευθύνσεις των κόμβων της δομής. Ο κόμβος είναι μια βασική μονάδα που χρησιμοποιείται για τη δημιουργία μιας δομής δεδομένων. Κάθε κόμβος περιέχει δεδομένα αλλά και πληροφορίες προς τους άλλους κόμβους. Οι πληροφορίες αυτές ονομάζονται δείκτες και είναι τα συνδετικά στοιχεία των κόμβων. Ένας δείκτης είναι η διεύθυνση του επόμενου κόμβου. Σε μη γραμμικές δομές όπως είναι τα δένδρα, έχουμε περισσότερα του ενός συνδετικά στοιχεία. Αυτά, είναι οι διευθύνσεις των συγγενικών κόμβων.
Γενικές παρατηρήσεις για τις δυναμικές γραμμές δεδομένων: Στις δυναμικές δομές, χρειάζεται να φυλάσσουμε ξεχωριστά τη διεύθυνση του πρώτου κόμβου (ή ενίοτε και του τελευταίου). Οι γραμμικές δομές δεδομένων που χρησιμοποιούνται στην κύρια μνήμη είναι ο πίνακας, η στοίβα, η ουρά και η λίστα. Στις δομές αυτές ορίζεται μια γραμμική σχέση διάταξης για δυο οποιαδήποτε διαδοχικά στοιχεία τους. Ουσιαστικά λοιπόν, οι δομές αυτές είναι μονοδιάστατες. Οι μη γραμμικές δομές δεδομένων χρησιμοποιούνται τόσο στην κύρια όσο και στη δευτερεύουσα μνήμη. Αυτές είναι τα δένδρα, ο σωρός και οι γράφοι. Οι σχέσεις των δεδομένων μεταξύ τους είναι σύνθετες και θα αναλυθούν εκτενέστερα στη συνέχεια. Οι βασικές λειτουργίες (ή πράξεις) που γίνονται σε μια δομή δεδομένων είναι: Προσπέλαση (access): είναι η πρόσβαση σε έναν κόμβο με σκοπό να εξεταστεί ή να τροποποιηθεί το περιεχόμενό του. Εισαγωγή (insertion): είναι η προσθήκη ή δημιουργία νέων κόμβων σε μια υπάρχουσα δομή. Διαγραφή (deletion): είναι η αφαίρεση κόμβων από μια υπάρχουσα δομή. Αποτελεί την αντίστροφη πράξη της εισαγωγής. Αναζήτηση (searching): είναι η εξέταση ενός συνόλου στοιχείων προκειμένου να εντοπιστούν ένα ή περισσότερα στοιχεία τα οποία έχουν μια δεδομένη ιδιότητα. Μεταβολή ή τροποποίηση (modification) του περιεχομένου ενός κόμβου μιας δομής. Προσάρτηση (append): είναι η προσάρτηση μιας δομής στο τέλος μιας άλλης με ίδιας μορφής κόμβους. Ταξινόμηση (sorting): είναι η διάταξη των στοιχείων μιας δομής κατά αύξουσα ή φθίνουσα τάξη. Συγχώνευση (merging): είναι η συνένωση δυο ή περισσότερων δομών σε μια ενιαία δομή. Διαχωρισμός (separation): είναι η διάσπαση μιας δομής σε δυο ή περισσότερες. Αποτελεί την αντίστροφη πράξη της συγχώνευσης.
3.1 Η ΣΤΟΙΒΑ ΩΣ ΑΤΔ Η στοίβα είναι μια μορφή οργάνωσης που απαντάται συχνά στην καθημερινή μας ζωή. Ως ένα απλό παράδειγμα δημιουργίας μιας στοίβας αναφέρουμε την τοποθέτηση εγγράφων το ένα πάνω στο άλλο. Θεωρώντας ότι τα πιο πρόσφατα έγγραφα είναι και τα πιο χρήσιμα, το έγγραφο που τοποθετήθηκε τελευταίο στη στοίβα θα είναι και το πρώτο που θα ανασυρθεί από αυτή. Έτσι, ως στοίβα (stack) μπορεί να χαρακτηρισθεί μια δομή δεδομένων στην οποία τα νέα δεδομένα τοποθετούνται και ανασύρονται από την κορυφή της. Υλοποιεί τη λειτουργία LIFO (Last In First Out) κατά την οποία: το πρώτο στοιχείο που εξάγεται από τη δομή είναι το τελευταίο που εισήχθη σε αυτήν. Ένα σύνολο από διαδικασίες (πράξεις) καθορίζει τη διεπαφή σε κάθε ΑΤΔ. Οι κυριότερες από αυτές για τη στοίβα είναι οι εξής: push( ): Προσθέτει ένα στοιχείο στην κορυφή της στοίβας. pop( ): Εξάγει (απομακρύνει) το πιο πρόσφατο στοιχείο από την κορυφή της στοίβας. gettop( ): Επιστρέφει το στοιχείο που βρίσκεται στην κορυφή της στοίβας χωρίς όμως να το απομακρύνει. isempty( ): Καθορίζει αν η στοίβα είναι κενή, δηλαδή αν δεν περιέχει κανένα στοιχείο. Η πιο απλή δομή δεδομένων που μπορεί να χρησιμοποιηθεί για την αναπαράσταση του ΑΤΔ στοίβα είναι ο πίνακας, δεδομένου ότι η στοίβα είναι μια συλλογή στοιχείων του ίδιου τύπου με γραμμική διάταξη. Όμως, η στοίβα διαφέρει από τον πίνακα ως προς το γεγονός ότι το πλήθος των στοιχείων του πίνακα είναι πάντα σταθερό, ενώ στη στοίβα μπορεί να αλλάζει. Κατά τη διάρκεια της εκτέλεσης μιας οποιασδήποτε λειτουργίας, υπάρχει ένας ακέραιος αριθμός ο οποίος δείχνει την τρέχουσα θέση της κορυφής (top) της στοίβας μέσα στον πίνακα. Αυτό ακριβώς απεικονίζεται στο Σχήμα 3.1.α: PUSH POP? S[N-1] E D C B A Σχήμα 3.1.α S[4] S[3] S[2] S[1] S[0] TOP Όπως φαίνεται στο Σχήμα 3.1.α πρώτο εισέρχεται το στοιχείο Α, μετά το Β και στη συνέχεια C, D, E με τη σειρά.
3.2 Η ουρά ως ΑΤΔ Η ουρά (queue) είναι γνωστή έννοια στην καθημερινότητά μας. Για παράδειγμα, λέμε ότι «είμαστε στην ουρά» όταν περιμένουμε να μπούμε στο λεωφορείο, να πληρώσουμε στο ταμείο ενός καταστήματος ή να εξυπηρετηθούμε από έναν υπάλληλο στην τράπεζα. Η στιγμή της εξυπηρέτησής μας καθορίζεται σύμφωνα με την ώρα της άφιξής μας στη συγκεκριμένη θέση αναμονής. Πιο συγκεκριμένα σύμφωνα με τη στιγμή που τοποθετηθήκαμε στη σειρά μας. Στη βιβλιογραφία η ουρά (queue) προσδιορίζεται ειδικότερα ως ουρά αναμονής. Στον προγραμματισμό, η ουρά (queue) είναι μια δομή δεδομένων στην οποία τα στοιχεία της εξέρχονται με την ίδια σειρά με την οποία εισήχθησαν. Εξάγεται πάντα το πιο παλαιό στοιχείο χρονικά. Η ουρά μπορεί να θεωρηθεί ως μια σειρά με δύο άκρα: το εμπρός (front) και το πίσω (rear). Ακολουθεί δε, τη μέθοδο εξυπηρέτησης FCFS (First Come First Served) ή αλλιώς FIFO (First In First Out). Σύμφωνα με τη μέθοδο αυτή εξασφαλίζεται με απλό τρόπο μία σειρά δίκαιης εξυπηρέτησης για όλα τα στοιχεία. Είναι φανερό ότι κάθε φορά εξυπηρετείται το στοιχείο το οποίο εμφανίστηκε πρώτο στην ουρά και άρα έχει παραμείνει περισσότερο χρόνο από τα υπόλοιπα. Μια FIFO δομή δεδομένων συχνά χρησιμοποιείται σε μια ακολουθία πολλαπλών απαιτήσεων για τη διάθεση ενός πόρου. Μπορούμε να αναφερθούμε χαρακτηριστικά στην περίπτωση της λειτουργίας ενός κοινόχρηστου εκτυπωτή. Παράδειγμα: α) Ας υποθέσουμε ότι έχουμε μια ουρά στην οποία έχουμε εισάγει τα στοιχεία 55, 77, 64 και 32. Έτσι θα έχουμε το ακόλουθο Σχήμα 3.2.α. 55 77 64 32 front = 0 rear = 3 Σχήμα 3.2.α β) Αν υποθέσουμε ότι διαγράφονται δυο στοιχεία, τότε θα συμβεί αυτό που απεικονίζεται στο Σχήμα 3.2.β: : 64 32 Σχήμα 3.2.β front = 2 rear = 3
γ) Προσθέτοντας δυο νέα στοιχεία, το 88 και το 49, η ουρά θα τροποποιηθεί όπως δείχνει και το Σχήμα 3.2.γ. 64 32 88 49 Σχήμα 3.2.γ front = 2 rear = 5 Βασικές λειτουργίες της ουράς: Οι βασικές πράξεις της ουράς υλοποιούνται με τη βοήθεια των αντίστοιχων συναρτήσεων, οι οποίες είναι: add ( ): Προσθέτει ένα στοιχείο στο πίσω άκρο της ουράς. delete ( ): Απομακρύνει ένα στοιχείο από το εμπρός άκρο της ουράς. isfull ( ): Ελέγχει αν η ουρά είναι γεμάτη. isempty ( ): Ελέγχει αν η ουρά είναι κενή, δηλαδή αν δεν περιέχει κανένα στοιχείο. Θα αναφερθούμε διεξοδικότερα στην επόμενη ενότητα τις δυο πιο σημαντικές λειτουργίες της ουράς δηλαδή την εισαγωγή (add) και την εξαγωγή (delete) ενός στοιχείου.