ΤΕΧΝΟΛΟΓΙΚΟ ΕΚΠΑΙΔΕΥΤΙΚΟ ΙΔΡΥΜΑ ΔΥΤΙΚΗΣ ΕΛΛΑΔΑΣ ΤΜΗΜΑ ΜΗΧΑΝΙΚΩΝ ΤΕ ΠΛΗΡΟΦΟΡΙΚΗΣ ΣΗΜΕΙΩΣΕΙΣ ΜΑΘΗΜΑΤΟΣ & ΕΡΓΑΣΤΗΡΙΑΚΕΣ ΑΣΚΗΣΕΙΣ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ Β ΕΞΑΜΗΝΟ ΣΠΟΥΔΩΝ Επιμέλεια Δρ Τσακνάκης Ιωάννης Επίκουρος Καθηγητής Ναύπακτος, 20 Φεβρουαρίου 2014
ΠΕΡΙΕΧΟΜΕΝΑ ΠΕΡΙΕΧΟΜΕΝΑ... 2 ΠΡΟΛΟΓΟΣ... 5 ΚΕΦΑΛΑΙΟ 1. ΑΛΓΟΡΙΘΜΟΙ ΚΑΙ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ... 7 1.1 Εισαγωγή... 7 1.2 Ορισμός του προβλήματος... 8 1.3 Επίλυση προβλημάτων Ανάπτυξη αλγορίθμων... 8 1.3.1 Ορισμός αλγορίθμου... 8 1.3.2 Αναπαράσταση αλγορίθμων... 9 1.3.3 Παράδειγμα ανάπτυξης αλγορίθμου... 11 1.3.4 Ανάλυση αλγορίθμων... 13 1.3.5 Μορφή ακολουθιών εντολών στους αλγορίθμους... 14 1.3.6 Είδη αλγορίθμων... 15 1.4 Δομές δεδομένων... 15 1.4.1 Βασικά παραδείγματα δομών δεδομένων... 16 Πίνακες (Arrays)... 16 Στοίβα (Stack)... 16 Ουρά (Queue)... 17 Γραμμικές Λίστες (Linear Lists)... 17 Δέντρα (Trees)... 17 Ουρά προτεραιότητας (Priority queue)... 18 Γράφοι (graphs)... 18 Ερωτήσεις- Ασκήσεις Κεφαλαίου... 18 ΚΕΦΑΛΑΙΟ 2. ΓΡΑΜΜΙΚΕΣ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ... 19 2.1 Πίνακες... 19 2.1.1 Μερικοί τύποι πινάκων... 20 2.1.2 Συμβολοσειρές... 21 2.2 Στοίβες... 22 2.2.1 Αλγόριθμος υλοποίησης στοίβας... 23 Πράξη Push... 24 Πράξη Pop... 24 2.2.2 Εφαρμογές Στοίβας Πολωνικός συμβολισμός... 25 Μετατροπή ένθετης παράστασης χωρίς παρενθέσεις σε μεταθετική... 26 Μετατροπή ένθετης παράστασης με παρενθέσεις σε μεταθετική... 27 Υπολογισμός παραστάσεις μεταθετικής μορφής... 28 2.3 Ουρές... 29 2.3.1 Αλγόριθμοι υλοποίησης ουράς... 30 Απλή μορφή υλοποίησης ουράς pipeline με χρήση πινάκων... 31 Ουρά δακτυλίου... 34 2.4 Λίστες... 34 2.4.1 Εφαρμογές... 35 2.4.2 Κατηγορίες λιστών... 35 2.4.3 Βασικές πράξεις σε λίστες... 36 2.4.4 Υλοποίηση αλγορίθμων των βασικών πράξεων... 37 Εισαγωγή στοιχείου... 38 Διαγραφή στοιχείου.... 40 2
Σάρωση λίστας... 41 Αναζήτηση στοιχείου... 42 Συνένωση λιστών... 42 Αντιστροφή λίστας... 43 Ερωτήσεις- Ασκήσεις Κεφαλαίου... 43 ΚΕΦΑΛΑΙΟ 3. ΜΗ ΓΡΑΜΜΙΚΕΣ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ... 45 3.1 Δένδρα... 45 3.1.1 Γενική περιγραφή - Ορισμοί... 45 3.1.2 Υλοποίηση δένδρου... 47 Υλοποίηση με πίνακα... 47 Υλοποίηση με εγγραφές... 48 3.1.3 Μέθοδοι διάσχισης δένδρων... 48 Προδιάταξη (preorder)... 48 Μεταδιάταξη (Postorder)... 49 Μεταδιάταξη (Postorder)... 49 Συμμετρική διάταξη (Symmetric order ή inorder)... 49 Εφαρμογή μετατροπής παραστάσεων στον πολωνικό συμβολισμό... 50 3.1.4 Νηματοειδή δένδρα... 50 3.1.5 Υλοποίηση αλγορίθμων των βασικών πράξεων... 51 Εισαγωγή (insert)... 52 Σάρωση δένδρου... 54 Αναζήτηση (search)... 54 Διαγραφή (deletion)... 55 3.1.6 Ισοζυγισμένα δένδρα... 57 3.2 Γράφοι... 57 3.2.1 Τι είναι γράφος... 57 3.2.2 Βασικοί κανόνες απεικόνισης γράφων... 57 3.2.3 Βασικές κατηγορίες γράφων... 59 Πολλαπλοί γράφοι... 59 Ισομορφικοί γράφοι... 60 Επίπεδοι γράφοι.... 60 Πλήρης γράφος... 61 Διμερής γράφος... 61 Συμπληρωματικοί γράφοι... 61 3.2.4 Διαπερατότητα... 62 Γράφος Euler... 63 Γράφος Hamilton... 63 Συνεκτικός γράφος... 63 Κατευθυνόμενοι και μη κατευθυνόμενοι γράφοι... 64 3.2.5 Συνεκτικότητα... 65 Ερωτήσεις- Ασκήσεις Κεφαλαίου... 65 ΚΕΦΑΛΑΙΟ 4. ΑΝΑΖΗΤΗΣΗ ΚΑΙ ΤΑΞΙΝΟΜΗΣΗ ΣΤΟΙΧΕΙΩΝ... 68 4.1 Αναζήτηση... 68 4.1.1 Γραμμική αναζήτηση (sequential search)... 68 4.1.2 Αναζήτηση κατά ομάδες (Block search)... 69 4.1.3 Δυαδική αναζήτηση (Binary search)... 70 4.1.4 Αναζήτηση παρεμβολής (interpolation search)... 70 4.2 Ταξινόμηση... 71 3
4.2.1 Ταξινόμηση Φυσαλίδας (Bubble Sort)... 72 4.2.2 Ταξινόμηση με Εισαγωγή (Insertion Sort)... 73 4.2.3 Ταξινόμηση με Επιλογή (Selection Sort)... 74 4.2.4 Ταξινόμηση Σωρού (Heap Sort)... 74 4.2.5 Ταξινόμηση με Συγχώνευση (Merge Sort)... 75 4.2.6 Γρήγορη Ταξινόμηση (Quick Sort)... 75 4.2.7 Ταξινόμηση με Μέτρηση (Counting Sort)... 76 4.2.8 Radix Sort... 77 4.2.9 Εξωτερική Ταξινόμηση (External Sorting)... 77 Ερωτήσεις- Ασκήσεις Κεφαλαίου... 77 ΠΑΡΑΡΤΗΜΑ Α. ΕΡΓΑΣΤΗΡΙΑΚΕΣ ΑΣΚΗΣΕΙΣ... 79 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 1.Αλγορίθμοι Πίνακες... 80 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 2. Στοίβες... 82 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 3. Εφαρμοφές Στοίβας... 83 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 4. Ουρές... 84 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 5. Λίστες... 88 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 6. Δημιουργία δυαδικού κομβοπροσανατολισμένου δένδρου αναζήτησης... 90 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 7. Υλοποίηση βασικών πράξεων σε δυαδικό κομβοπροσανατολισμένο δένδρο αναζήτησης... 92 ΕΡΓΑΣΤΗΡΙΑΚΗ ΑΣΚΗΣΗ 8. Δημιουργία ισοζυγισμένου δυαδικού κομβοπροσανατολισμένου δένδρου αναζήτησης... 95 ΒΙΒΛΙΟΓΡΑΦΙΑ... 97 4
ΠΡΟΛΟΓΟΣ Ο προγραμματισμός επικεντρώνεται στην μετατροπή της ακολουθίας βημάτων επίλυσης ενός προβλήματος σε μία ακολουθία εντολών άμεσα αναγνωρίσιμη από τον υπολογιστή. Γενικά θα μπορούσε να θεωρηθεί ότι η επίλυση ενός προβλήματος απαιτεί 3 στάδια, τον σαφή ορισμό του προβλήματος, την λύση του προβλήματος μέσω μίας ακριβής ακολουθίας βημάτων και την εισαγωγή της επίλυσης του προβλήματος (βημάτων) στον υπολογιστή και την επίλυση του (πρόγραμμα). Παρά το γεγονός ότι ο προγραμματισμός αναφέρεται στο τρίτο στάδιο πρέπει να κατανοηθεί ότι τα πρώτα δύο στάδια είναι εξίσου σημαντικά. Η μεθοδολογία ανάπτυξης της επίλυσης του προβλήματος αποτελεί τον αλγόριθμο ενώ ο τρόπος με τον οποίο οργανώνονται τα δεδομένα σε ομάδες ονομάζονται δομές δεδομένων. Οι αλγόριθμοι και οι δομές δεδομένων αποτελούν τα βασικά συστατικά ενός προγράμματος. Οι τεχνικές που θα χρησιμοποιηθούν στην υλοποίηση του αλγορίθμου και η επιλογή των κατάλληλων δομών δεδομένων προδιαγράφουν ουσιαστικά την ποιότητα και την απόδοση του προγράμματος πριν ακόμα αυτό υλοποιηθεί. Αντικείμενο των σημειώσεων αποτελεί η εισαγωγή των σπουδαστών στις βασικές αρχές των Δομών Δεδομένων, να παρουσιάσει τις πιο βασικές από αυτές που χρησιμοποιούνται τόσο στην κύρια όσο και στην δευτερεύουσα μνήμη. Ιδιαίτερα, οι σημειώσεις επικεντρώνονται στις πράξεις των δομών δεδομένων αναλύοντας τους αλγορίθμους υλοποίησης τους. Πιο συγκεκριμένα, στο Κεφάλαιο 1, γίνεται μία σύντομη παρουσίαση των βασικών εννοιών των αλγορίθμων και των δομών δεδομένων. Αρχικά τονίζονται οι βασικές αρχές ορισμού ενός προβλήματος. Στην συνέχεια ορίζεται η μεθοδολογία ανάπτυξης της επίλυσης ενός προβλήματος εισάγοντας την έννοια του αλγορίθμου. Δίνεται ως παράδειγμα ο αλγόριθμος επίλυσης του προβλήματος εύρεσης του μέγιστου κοινού διαιρέτη. Παράλληλα περιγράφεται ο τρόπος ανάλυσης των αλγορίθμων και της μέτρησης της απόδοσης/πολυπλοκότητας τους ενώ παρουσιάζεται και η μορφή των ακολουθιών εντολών στους αλγορίθμους. Παρακάτω περιγράφεται ο τρόπος με τον οποίο οργανώνονται τα δεδομένα σε ομάδες που ονομάζονται δομές δεδομένων για την αποτελεσματική χρήση τους στην ανάπτυξη των αλγορίθμων αλλά και στην διαδικασία του προγραμματισμού στη συνέχεια. Αρχικά περιγράφονται οι συνηθισμένες λειτουργίες πάνω στις δομές δεδομένων και στη συνέχεια γίνεται μία συνοπτική παρουσίαση των σπουδαιότερων από αυτές. Στο Κεφάλαιο 2, παρουσιάζονται οι πιο βασικές γραμμικές δομές δεδομένων που χρησιμοποιούνται τόσο στην κύρια όσο και στην δευτερεύουσα μνήμη. Πιο συγκεκριμένα, αρχικά περιγράφεται η πιο απλή και συχνά χρησιμοποιούμενη δομή, ο πίνακας. Στη συνέχεια αναλύεται η στοίβα, οι βασικές αρχές λειτουργίας της και οι βασικές πράξεις push και pop. Παράλληλα, περιγράφονται μερικές εφαρμογές της στοίβας όπως είναι η μετατροπή παραστάσεων στον πολωνικό συμβολισμό. Αμέσως μετά περιγράφεται η ουρά, οι διαφορές της με την στοίβα και οι βασικές της πράξεις Dequeue και Enqueue. Τέλος, αναλύονται οι λίστες δίνοντας έμφαση στις πράξεις εισαγωγής, αναζήτησης και διαγραφής σε απλή διασυνδεμένη λίστα. Σε όλες τις παραπάνω δομές δίνονται αλγόριθμοι υλοποίησης των βασικών πράξεων. Στο Κεφάλαιο 3, περιγράφονται οι μη γραμμικές δομές όπως τα δέντρα και οι γράφοι. Αρχικά δίνεται ιδιαίτερη έμφαση στα δένδρα ως οι πιο βασικές δομές δεδομένων στην υλοποίηση εφαρμογών λόγω της αποδοτικότητας τους στην εκτέλεση πράξεων σε μεγάλου όγκου δεδομένα. Ιδιαίτερα παρουσιάζεται η 5
λειτουργία και οι βασικές πράξεις σε ένα δυαδικό κομβοπροσανατολισμένο δένδρο αναζήτησης. Στη συνέχεια περιγράφονται οι βασικές κατηγορίες γράφων καθώς και βασικές έννοιες τους όπως η διαπερατότητα και η συνεκτικότητα τους. Στα προηγούμενα κεφάλαια περιγράψαμε τον τρόπο με τον οποίο τα δεδομένα οργανώνονται μεταξύ τους και ομαδοποιούνται σε δομές δεδομένων ώστε στη συνέχεια να μπορούν να διαχειριστούν βέλτιστα και να γίνουν σ αυτά όλες απαιτούμενες πράξεις. Στην περιγραφή κάθε δομής δεδομένων αναλύσαμε τις πράξεις ένθεσης και απώθησης χωρίς να δώσουμε ιδιαίτερη βαρύτητα στις πράξεις αναζήτησης και ταξινόμησης που αποτελούν και το αντικείμενο αυτού του κεφαλαίου. Στο κεφάλαιο 4 αρχικά θα αναλύσουμε τις πιο βασικές μεθόδους αναζήτησης που εφαρμόζονται τόσο στην κύρια μνήμη όσο και στην βοηθητική. Στη συνέχεια θα αναλύσουμε την πράξη της ταξινόμησης και τις μεθόδους που χρησιμοποιούνται και χωρίζονται στις βασιζόμενες στις συγκρίσεις (comparison based) και σε εκείνες που βασίζονται στην πληροφορία εισόδου. 6
ΚΕΦΑΛΑΙΟ 1. ΑΛΓΟΡΙΘΜΟΙ ΚΑΙ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ Σκοπός του κεφαλαίου αυτού είναι να γίνει μία σύντομη παρουσίαση των σπουδαστών στις βασικές έννοιες των αλγορίθμων και των δομών δεδομένων. Αρχικά τονίζονται οι βασικές αρχές ορισμού ενός προβλήματος. Στην συνέχεια ορίζεται η μεθοδολογία ανάπτυξης της επίλυσης ενός προβλήματος εισάγοντας την έννοια του αλγορίθμου. Δίνεται ως παράδειγμα ο αλγόριθμος επίλυσης του προβλήματος εύρεσης του μέγιστου κοινού διαιρέτη. Παράλληλα περιγράφεται ο τρόπος ανάλυσης των αλγορίθμων και της μέτρησης της απόδοσης/πολυπλοκότητας τους ενώ παρουσιάζεται και η μορφή των ακολουθιών εντολών στους αλγορίθμους. Παρακάτω περιγράφεται ο τρόπος με τον οποίο οργανώνονται τα δεδομένα σε ομάδες που ονομάζονται δομές δεδομένων για την αποτελεσματική χρήση τους στην ανάπτυξη των αλγορίθμων αλλά και στην διαδικασία του προγραμματισμού στη συνέχεια. Αρχικά περιγράφονται οι συνηθισμένες λειτουργίες πάνω στις δομές δεδομένων και στη συνέχεια γίνεται μία συνοπτική παρουσίαση των σπουδαιότερων από αυτές. 1.1 Εισαγωγή Αρκετές φορές, στα προηγούμενα κεφάλαια, έχουν αναφερθεί οι όροι προγραμματισμός, προγράμματα ως ακολουθίες εντολών, πολυπρογραμματισμός, γλώσσες προγραμματισμού, γλώσσα μηχανής κ.α. Ο προγραμματισμός στους υπολογιστές είναι η διαδικασία με την οποία ο χρήστης ορίζει στον υπολογιστή τον τρόπο με τον οποίο θέλει να επιλύσει ένα συγκεκριμένο πρόβλημα ή κατάσταση. Επομένως βασικότερη έννοια είναι η επίλυση ενός προβλήματος από τον χρήστη με τέτοιο τρόπο ώστε να μπορεί στη συνέχεια να εκτελέσει τα βήματα επίλυσης ο υπολογιστής. Γενικά θα μπορούσε να θεωρηθεί ότι η επίλυση ενός προβλήματος απαιτεί 3 στάδια: 1. Τον σαφή ορισμό του προβλήματος. 2. Την λύση του προβλήματος μέσω μίας ακριβής ακολουθίας βημάτων 3. Την εισαγωγή της επίλυσης του προβλήματος (βημάτων) στον υπολογιστή και την επίλυση του Ο προγραμματισμός επικεντρώνεται στο 3 στάδιο, δηλαδή στην μετατροπή της επίλυσης του προβλήματος και της ακολουθίας βημάτων είτε σε μία ακολουθία εντολών άμεσα αναγνωρίσιμη από τον υπολογιστή είτε σε μία ενδιάμεση γλώσσα εύχρηστη και φιλική από τον χρήστη η οποία μπορεί στη συνέχεια να μεταγλωττισθεί σε εντολές που αναγνωρίζει ο υπολογιστής και ανήκουν στην γλώσσα μηχανής του συγκεκριμένου υπολογιστή. Παρά το γεγονός ότι ο προγραμματισμός αναφέρεται στο τρίτο στάδιο πρέπει να κατανοηθεί ότι τα πρώτα δύο στάδια είναι εξίσου σημαντικά. Έτσι στο κεφάλαιο αυτό θα επικεντρωθούμε και στα τρία στάδια. Αρχικά θα περιγραφούν οι βασικές αρχές με τις οποίες πρέπει να ορίζεται ένα πρόβλημα. Στην συνέχεια θα οριστεί η μεθοδολογία ανάπτυξης της επίλυσης του προβλήματος εισάγοντας την έννοια του αλγορίθμου. Παράλληλα θα περιγραφεί ο 7
τρόπος με τον οποίο οργανώνονται τα δεδομένα σε ομάδες που ονομάζονται δομές δεδομένων για την αποτελεσματική χρήση τους στην ανάπτυξη των αλγορίθμων αλλά και στην διαδικασία του προγραμματισμού στη συνέχεια. Τέλος θα δοθεί ιδιαίτερη σημασία στις γλώσσες προγραμματισμού που αποτελούν τη γλώσσα επικοινωνίας του χρήστη με τον υπολογιστή. 1.2 Ορισμός του προβλήματος Κάθε φορά που ο χρήστης καλείται να λύσει ένα πρόβλημα, δηλαδή μία κατάσταση που απαιτεί λύση, πρέπει να ακολουθήσει μία σειρά κανόνων ή βημάτων. Οι κανόνες αυτοί σκοπό έχουν την εξεύρεση της βέλτιστης λύσης και είναι 1. Κατανόηση του προβλήματος Είναι σαφές ότι βασική παράμετρος σωστής λύσης του προβλήματος είναι η κατανόηση του. Αυτό σημαίνει ότι αρχικά ο χρήστης πρέπει να το ερμηνεύσει σωστά και στη συνέχεια να το διατυπώσει προφορικά ή γραπτά με σαφήνεια χρησιμοποιώντας σωστή ορολογία και σύνταξη. 2. Αποτύπωση δομής του προβλήματος Μετά την κατανόηση του προβλήματος ο χρήστης πρέπει να το αναλύσει βρίσκοντας τα συστατικά του μέρη και στη συνέχεια εξετάζοντας καθένα χωριστά αλλά και τις σχέσεις επικοινωνίας μεταξύ τους. Η ανάλυση αυτή ουσιαστικά απλοποιεί την επίλυση του προβλήματος αφού απαιτεί τη λύση μικρότερων τμημάτων και την ένωση μετά όλων των λύσεων. 3. Καθορισμός απαιτήσεων Στην περίπτωση αυτή καλείται ο χρήστης να προσδιορίζει τα δεδομένα του προβλήματος καθώς και τα ζητούμενα του. Την είσοδο και την έξοδο του προβλήματος. Τα προβλήματα μπορούν να κατηγοριοποιηθούν σε πολλές κατηγορίες. Έτσι μπορεί να είναι άλυτα, επιλύσιμα ή ανοιχτά ανάλογα με τη δυνατότητα επίλυσης τους είτε δομημένα, ημιδομημένα ή αδόμητα ανάλογα με το βαθμό δόμησης τους. Το σημαντικό όμως σε κάθε περίπτωση είναι ότι ο χρήστης προγραμματιστής δεν καλείται πάντα να επιλύσει ένα μαθηματικό ή στατιστικό πρόβλημα που έχει και μία συγκεκριμένη μαθηματική επίλυση με πράξεις. Είναι σύνηθες ο χρήστης να καλείται να επιλύσει ένα σύνθετο πρόβλημα με βάση τους συλλογισμούς του και τη σκέψη του. Στην περίπτωση αυτή πρέπει να ακολουθήσει όλους τους παραπάνω κανόνες ώστε να ορίσει το πρόβλημα σωστά. 1.3 Επίλυση προβλημάτων Ανάπτυξη αλγορίθμων 1.3.1 Ορισμός αλγορίθμου Μετά τον ορισμό του προβλήματος ακολουθεί η επίλυση του. Αυτή επιτυγχάνεται με τον ορισμό μία πεπερασμένης σειράς ενεργειών-βημάτων που αποτελούν μία σαφή υπολογιστική διαδικασία και εκτελείται σε πεπερασμένο χρόνο. Η διαδικασία αυτή καλείται αλγόριθμος. Σε κάθε αλγόριθμο πρέπει να καθορίζονται τα παρακάτω στοιχεία: 1. Είσοδος (input). Απεικονίζει τις τιμές των δεδομένων του προβλήματος που δίνονται από την αρχή ή παράγονται από ενδιάμεσα τμήματα του και 8
αποτελούν είσοδο για τα επόμενα (και ονομάζονται στιγμιότυπα). Όταν τα δεδομένα εισόδου καλύπτουν κάθε φορά τις προδιαγραφές του συστήματος τότε η είσοδος του αλγορίθμου καλείται νόμιμη. 2. Έξοδος (output). Η έξοδος του αλγορίθμου είναι η μερική ή τελική λύση του προβλήματος. 3. Πεπερασμένα βήματα. Κάθε αλγόριθμος πρέπει να αποτελείται από συγκεκριμένα βήματα και να ολοκληρώνεται μετά την εκτέλεση τους. 4. Σαφή απόδοση. Το γεγονός αυτό σημαίνει ότι ο χρήστης προγραμματιστής πρέπει να γνωρίζει την ταχύτητα εκτέλεσης του αλγορίθμου του. Δηλαδή το πλήθος των πράξεων που εκτελεί για την επίλυση του προβλήματος ώστε να μπορεί να τον αξιολογήσει σε σύγκριση με άλλους αντίστοιχους για το ίδιο πρόβλημα. 1.3.2 Αναπαράσταση αλγορίθμων Κάθε αλγόριθμος πρέπει στο τέλος να εκφραστεί από τον προγραμματιστή σε μία γλώσσα προγραμματισμού ώστε να εισαχθεί και να εκτελεστεί στον υπολογιστή. Η διαδικασία της άμεσης σύνταξης του αλγορίθμου στη γλώσσα προγραμματισμού εγκυμονεί πολλούς κινδύνους και δεν ενδείκνυται ανεξάρτητα της εμπειρίας του προγραμματιστή. Κάθε αλγόριθμος πρέπει να συνταχθεί και να περάσει από πολλά στάδια μέχρι να εισαχθεί στον υπολογιστή. Ο τρόπος αυτός περιλαμβάνει και όλα τα στάδια ελέγχου της ορθότητας του ανάλογα σε πιο επίπεδο δομής βρίσκεται. Οι τρόποι αναπαράστασης ενός αλγορίθμου με βάση την σειρά ανάπτυξης του είναι: 1. Ελεύθερο κείμενο. Ο προγραμματιστής αρχικά καταγράφει τους συλλογισμούς του σε φυσική γλώσσα. 2. Δομημένη φυσική γλώσσα. Στη συνέχεια γίνεται η δόμηση του κειμένου σε βήματα ακολουθώντας την δόμηση της επίλυσης του προβλήματος (τα τμήματα του). 3. Αναπαράσταση με διαγράμματα. Αμέσως μετά ο προγραμματιστής αναπαριστά τα βήματα του αλγορίθμου του χρησιμοποιώντας διαγράμματα ροής (flow charts) ή όπως αλλιώς ονομάζονται λογικά διαγράμματα. Η σχηματική αυτή αναπαράσταση βοηθά τον προγραμματιστή να εξετάσει τις αλληλεπιδράσεις των τμημάτων μεταξύ τους και να έχει μία καλύτερη εποπτική εικόνα του αλγορίθμου του. Σύμφωνα με μία κοινά αποδεκτή διαγραμματική τεχνική οι βασικές εντολές βήματα του αλγορίθμου αναπαρίστανται με γεωμετρικά σχήματα ως εξής: Η έλλειψη δηλώνει την αρχή και το τέλος του αλγορίθμου Το ορθογώνιο δηλώνει εντολές εκτέλεσης πράξεων Το πλάγιο παραλληλόγραμμο δηλώνει την είσοδο δεδομένων ή την έξοδο των αποτελεσμάτων των αλγορίθμων, Ο ρόμβος δηλώνει μία διαδικασία ερώτησης ή απόφασης. 4. Ψευδοκώδικα ή ψευδογλώσσα. Αποτελείται από μία μίξη εντολών υπαρχουσών γλωσσών προγραμματισμού και φυσικής γλώσσας χωρίς να απαιτείται λεκτικός, συντακτικός και σημασιολογικός έλεγχος του κώδικα 9
αυτού. Επικεντρώνεται κυρίως στην ανάπτυξη του αλγορίθμου αναπαριστώντας με σαφή καθορισμένο τρόπο όλων των εντολών επίλυσης του προβλήματος. Ένας αλγόριθμος είναι σύνηθες να έχει την παρακάτω δομή: Α. Αρχικά δίνεται το όνομα του αλγορίθμου: Π.χ. Αλγόριθμος Επίλυση Προβλήματος. B. Αμέσως μετά ορίζονται τα δεδομένα τα οποία αποθηκεύονται σε λογικές μεταβλητές ή σταθερές όπως Boolean που παίρνουν τιμές σωστού ή λάθους. Π.χ. Δεδομένα //α,β : ακέραιοι αριθμοί; γ: ακέραια μεταβλητή// Γ. Στη συνέχεια ξεκινά ο αλγόριθμος με μία λέξη όπως Αρχή και ολοκληρώνεται μέχρι την λέξη εντολή Τέλος. Κάθε αλγόριθμος χρησιμοποιεί ένα πλήθος εντολών αλλά και τελεστών. Πιο συγκεκριμένα: Τελεστές. Οι τελεστές μπορεί να είναι τα αριθμητικά σύμβολα των πράξεων όπως +, -, *, /, ^, συγκριτικοί όπως <, >, =,,, αλλά και λογικοί όπως και (σύζευξη), ή (διάζευξη, όχι (άρνηση). Λογικές υποθέσεις, δηλαδή εντολές της μορφής: Π.χ. Αν Συνθήκη Τότε Α Αλλιώς Β Εντολές καταχώρησης, επανάληψης, επιλογής κ.α. Π.χ. Επαναλάμβανε ή ή ή Α Μέχρι (συνθήκη) Επαναλάμβανε Αν Συνθήκη Έξοδος Για μεταβλητή = αρχή Mέχρι μεταβλητή = τέλος Βήμα τιμή Κάνε A Επέλεξε (έκφραση) Περίπτωση 1: A Περίπτωση 2: B Αλλιώς : X Σε κάθε μία από τις παραπάνω εντολές απαιτείται στο τέλος τους η λέξη : Τέλος (Εντολής) Είναι γεγονός ότι η εκτέλεση όλων των παραπάνω βημάτων βρίσκεται στην ευχέρεια του προγραμματιστή και είναι σύνηθες να μην εκτελούνται. Όμως πρέπει να τονιστεί η σπουδαιότητα τουλάχιστον του τελευταίου βήματος και της σύνταξης ψευδοκώδικα πριν την μετατροπή του σε μια γλώσσα προγραμματισμού. Η 10
διαδικασία αυτή είναι τόσο σημαντική που τα τελευταία χρόνια έχει οριστεί ως γνωστικό αντικείμενο με θέμα μηχανική των αλγορίθμων (algorithm engineering). 1.3.3 Παράδειγμα ανάπτυξης αλγορίθμου Έστω έχουμε το πρόβλημα: «Δοθέντων 2 ακεραίων αριθμών x, y να βρεθεί ο Μέγιστος Κοινός Διαιρέτης (ΜΚΔ) αυτών z» Α. Αρχικά προσπαθούμε να κατανοήσουμε το πρόβλημα καταγράφοντας τους συλλογισμούς σε φυσική γλώσσα. Βρίσκουμε λύσεις και εκτελούμε παραδείγματα ώστε να δούμε αν αυτές είναι σωστές. Τέλος επιλέγουμε την λύση που μαθηματικά ή έστω διαισθητικά καταλαβαίνουμε ότι είναι η αποδοτικότερη. Στο παράδειγμα επομένως μπορούμε να θεωρήσουμε τις εξής λύσεις: 1. Από τους δύο αριθμούς μπορούμε να επιλέξουμε τον μικρότερο έστω x και να εξετάσουμε από το 1 έως το x, όλους τους αριθμούς κρατώντας αυτούς που διαιρούν το y. O μεγαλύτερος από αυτούς είναι και ο ζητούμενος. Είναι σαφές ότι σε κάθε περίπτωση θα εκτελεστεί μία εντολή επανάληψης για x φορές όπου κάθε φορά θα εξετάζεται αν ο τρέχον αριθμός διαιρεί τον y, και αν ναι τον κρατούμε και πάμε στον επόμενο αριθμό. 2. Μία άλλη λύση είναι μία παραλλαγή της παραπάνω μόνο που μπορούμε να ξεκινήσουμε την επανάληψη από τον αριθμό x κατεβαίνοντας μέχρι το 1. Στην περίπτωση αυτή έχουμε καλύτερη λύση όμως ο χρόνος της χειρότερης περίπτωσης όπως θα δούμε παρακάτω είναι ίδιος. 3. Μία Τρίτη λύση είναι η εξής: Διαιρούμε το x με το y, και έστω z το υπόλοιπο. Αν z = 0 τότε ο ΜΚΔ είναι ο y. Αν z 0 τότε επανάλαβε το βήμα με τους ακεραίους y και z αντί για x και y. Η λύση αυτή είναι γνωστή ως αλγόριθμος του Ευκλείδη. Για να κατανοήσουμε καλύτερα τη λύση αυτή και να ορίσουμε τα πεπερασμένα βήματα του αλγορίθμου που προκύπτουν από αυτήν μπορούμε να εκτελέσουμε βήμα - βήμα ένα συγκεκριμένο παράδειγμα. Π.χ. Έστω έχουμε τους αριθμούς 26 και 65 και ψάχνουμε τον ΜΚΔ τους. Έχουμε τον πίνακα που προκύπτει από την διαδοχική επίλυση του αλγορίθμου. Επανάληψη Εκτέλεση Βημάτων x y z Αρχή 1. z = y άρα z = 65 26 65 65 1 η 1. z = x mod y άρα z = 26 2. x = y άρα x = 65 3. y = z άρα y = 26 2 η 1. z = x mod y άρα z = 13 2. x = y άρα x = 26 65 26 26 26 13 13 11
3. y = z άρα y = 13 3 η 1. z = x mod y άρα z = 0 2. x = y άρα x = 13 3. y = z άρα y = 0 Αποτέλεσμα ΜΚΔ = x = 13 13 0 0 Στην αρχή βάζουμε z = y γεγονός που σημαίνει ότι αν μπούμε στην επανάληψη για όσο το z 0 σημαίνει ότι τουλάχιστον στην αρχή y 0 άρα εκτελείται και η πράξη x mod y. Είναι σαφές ότι η παραπάνω λύση είναι αποδοτικότερη από τις προηγούμενες δύο. Β. Σύμφωνα με την παράγραφο 6.3.2, το επόμενο βήμα είναι η αναπαράσταση των πεπερασμένων βημάτων επίλυσης του αλγορίθμου με το αντίστοιχο διάγραμμα ροής. Για το συγκεκριμένη λύση του παραδείγματος δίνεται το παρακάτω διάγραμμα ροής: ΑΡΧΗ ΔΙΑΒΑΣΕ Χ,Υ Z = Y Z = 0 ΝΑΙ ΟΧΙ Z = X MOD Y ΤΥΠΩΣΕ Χ X = Y ΤΕΛΟΣ Y = Z Γ. Το επόμενο βήμα είναι σύνταξη του αλγορίθμου με την μορφή ψευδοκώδικα ή ψευδογλώσσας. Με τον τρόπο αυτό θα έχουμε τον παρακάτω αλγόριθμο: Αλγόριθμος ΜΚΔ_Ευκλείδη 12
Δεδομένα //x, y : ακέραιοι αριθμοί; z: ακέραια μεταβλητή// Αρχή Τέλος z = y Όσο z 0 εκτέλεσε z = x mod y x = y y = z Τελος_Επανάληψης Αποτελέσματα // τύπωσε x : O x είναι ο ΜΚΔ// Τέλος ΜΚΔ_Ευκλείδη Δ. Το επόμενο και τελευταίο βήμα είναι η σύνταξη και μετατροπή του αλγορίθμου σε μία γλώσσα προγραμματισμού υψηλού επιπέδου. Η υλοποίηση του αφήνεται στον σπουδαστή κατά την πρώτη εργαστηριακή άσκηση (Παράρτημα Α). 1.3.4 Ανάλυση αλγορίθμων Όπως τονίστηκε παραπάνω, κάθε αλγόριθμος πρέπει να έχει πεπερασμένο αριθμό βημάτων και μετρήσιμη απόδοση. Η απόδοση ενός αλγορίθμου εξαρτάται από τους πόρους που απαιτεί ο αλγόριθμος από το υπολογιστικό σύστημα και αυτοί μπορεί να είναι ο χώρος της μνήμης που χρησιμοποιείται για την αποθήκευση των δεδομένων καθώς και ο χρόνος που χρειάζεται ο αλγόριθμος για την εκτέλεση των εντολών του αλγορίθμου. Από αυτές τις μονάδες μέτρησης ορίζεται η πολυπλοκότητα χρόνου (time complexity) και χώρου (space complexity) ως δείκτες απόδοσης του αλγορίθμου. Για την μέτρηση των παραπάνω δεικτών έχουν οριστεί μοντέλα υπολογισμού τα οποία θεωρούν ότι ο αλγόριθμος εκτελείται σε μία συγκεκριμένη υπολογιστική μηχανή. Βασικό παράδειγμα μοντέλου υπολογισμού αποτελεί η Μηχανή Turing καθώς και το μοντέλο μηχανής τυχαίας προσπέλασης (RAM) που αναφέρεται σε συστήματα του ενός επεξεργαστή γενικού σκοπού, δίχως τη δυνατότητα ταυτόχρονης εκτέλεσης πράξεων. Στο μοντέλο αυτό ο χρόνος και ο χρόνος ενός αλγορίθμου επί συγκεκριμένης εισόδου καλείται, αντιστοίχως, ο αριθμός των στοιχειωδών πράξεων ή βημάτων που εκτελούνται και ο αριθμός των θέσεων μνήμης που απαιτούνται. Μπορούμε εύκολα να καταλάβουμε τις πολυπλοκότητες ενός αλγορίθμου αφού: 1. Κάθε εντολή ανάθεσης τιμής, δήλωσης απλής μεταβλητής, η λογική/αριθμητική πράξη κοστίζει σταθερό χρόνο που συμβολίζεται Ο(1). 2. Σε κάθε βρόγχο επιλογών σε κάθε επανάληψη έχουμε ανεξάρτητα το κόστος όλων των πράξεων σ αυτές. 3. Η δέσμευση μεταβλητής απλού τύπου κοστίζει σταθερό χώρο Ο(1) ενώ η δέσμευση ενός πίνακα κ θέσεων κοστίζει χρόνο και χώρο Ο(κ). Αν n είναι το μέγεθος του προβλήματος (π.χ. ζητάτε να ταξινομηθούν n αριθμοί) τότε η πολυπλοκότητα χρόνου ή και χώρου των αλγορίθμων θα έχουν μία μορφή σαν τις επόμενες: 13
O(1) : Σταθερή. Ο(logn) : Λογαριθμική Ο(n) : Γραμμική. Ο(n 2 ) : Τετραγωνική. O(n 3 ) : Κυβική. O(2 n ) : Εκθετική. κ.α. Μία συνηθισμένη μονάδα μέτρησης των δεικτών είναι μέσω της ανάλυσης χειρότερης περίπτωσης κατά την οποία ο χρόνος ή ο χώρος μπορεί να πάρει την μέγιστη (χειρότερη) τιμή για οποιαδήποτε είσοδο. Αντίστοιχα μέσω της κατανομής χρόνου ή χώρου στο σύνολο των στιγμιότυπων του αλγορίθμου έχουμε την ανάλυση μέσης ή αναμενόμενης περίπτωσης (average, expected case analysis). Τέλος συναντάται συχνά η ανάλυση επιμερισμένης περίπτωσης (amortized case analysis) κατά την οποία ο χρόνος ή χώρος χειρότερης περίπτωσης κατανέμεται σε μία ακολουθία πράξεων ή λειτουργιών. 1.3.5 Μορφή ακολουθιών εντολών στους αλγορίθμους Ο τρόπος με τον οποίο δομούνται οι εντολές ενός αλγορίθμου και ορίζονται οι αντίστοιχες ακολουθίες τους βασίζονται σε συγκεκριμένες τεχνικές είτε απλές, όπως στην περίπτωση σειριακών εντολών είτε πολύπλοκες όπως στην περίπτωση αναδρομών και εμφωλιασμού. Πιο συγκεκριμένα οι εντολές μπορούν να δομηθούν με τους παρακάτω τρόπους: 1. Σειριακή ακολουθία εντολών Χρησιμοποιείται για την επίλυση απλών προβλημάτων 2. Επιλογή εντολών Είναι πολύ συνηθισμένη η περίπτωση να πρέπει να εκτελεστεί μία εντολή ανάλογα με κάποια δεδομένα εισόδου τα οποία καλούνται κριτήρια. 3. Πολλαπλές επιλογές Στην περίπτωση αυτή με βάση μία τιμή ενός δεδομένου ο έλεγχος- εκτέλεση του αλγορίθμου επιλέγεται να προχωρήσει από μία λίστα συγκεκριμένων περιπτώσεων. 4. Εμφωλιασμένες εντολές Είναι εντολές που ανήκουν σε μία ομάδα και καλούνται να εκτελεστούν σε ένα επίπεδο πιο χαμηλά σε σχέση με ένα άλλο επίπεδο ομάδας εντολών. 5. Επανάληψη εντολών Σε πολλές περιπτώσεις απαιτείται η εκτέλεση μίας ή περισσοτέρων εντολών περισσότερες από μια φορά. 14
1.3.6 Είδη αλγορίθμων Οι αλγόριθμοι μπορούν να ταξινομηθούν σε πολλές κατηγορίες ανάλογα την απόδοση της λύσης, των τεχνικών υλοποίησης που χρησιμοποιούν αλλά και της θεματικής περιοχής των προβλημάτων που καλούνται να επιλύσουν. Έτσι ένας αλγόριθμος μπορεί να είναι βέλτιστος (optimal) όταν επιτυγχάνει βέλτιστη λύση πολυπλοκότητας επίλυσης του προβλήματος ενώ μπορεί να είναι προσεγγιστικός ή ευριστικός (Approximation ή Heuristic) όταν αυτοί προσεγγίζουν την άριστη λύση του προβλήματος για κάθε στιγμιότυπο του. Επίσης ένας αλγόριθμος μπορεί να είναι αναδρομικός (recursive). Στην περίπτωση αυτή ορίζεται μία αναδρομική σχέση και μία βάση. Π.χ. το παραγοντικό ενός αριθμού n δίνεται από την αναδρομική σχέση n! = n(n-1)! για n 1 ενώ η βάση είναι 0! = 1. Ο αλγόριθμος του Ευκλείδη για τον υπολογισμό του ΜΚΔ 2 ακεραίων αριθμών όπως παρουσιάστηκε παραπάνω μπορεί να μετατραπεί σε αναδρομικό ως εξής: Αλγόριθμος ΜΚΔ(x,y) Δεδομένα //x, y : ακέραιοι αριθμοί// Αρχή Τέλος Aν y>0 τότε εκτέλεσε ΜΚΔ(x, x mod y) Αποτελέσματα // τύπωσε x : O x είναι ο ΜΚΔ// Τέλος ΜΚΔ Πολλές φορές οι αλγόριθμοι μπορούν να εκτελεστούν σε ένα υπολογιστή ο οποίος έχει περισσότερες από μία ΚΜΕ αλλά και σε ένα σύνολο υπολογιστών με 1 ΚΜΕ. Στις περιπτώσεις αυτές υλοποιούνται παράλληλοι αλγόριθμοι. Επίσης συχνά ορίζονται οι αλγόριθμοι ανάλογα με την περιοχή των προβλημάτων που επιλύουν. Έτσι υπάρχουν οι αριθμητικοί αλγόριθμοι σε προβλήματα αριθμητικής ανάλυσης, οι συνδυαστικοί σε αντίστοιχα προβλήματα, οι στοχαστικοί σε προβλήματα θεωρίας ουρών κ.α. 1.4 Δομές δεδομένων Η διαχείριση των δεδομένων σε έναν υπολογιστή είναι μία πολύπλοκη διαδικασία. Ο προγραμματιστής αρχικά πρέπει να οργανώσει τα δεδομένα του με τέτοιο τρόπο ώστε να μπορεί στη συνέχεια με εύχρηστο τρόπο να εκτελέσει όλες τις απαιτούμενες πράξεις σ αυτά. Έτσι τα δεδομένα δομούνται σε ομάδες που ονομάζονται δομές δεδομένων Κάθε μία δομή δεδομένων έχει τα ιδιαίτερα της χαρακτηριστικά ανάλογα με τον τρόπο που είναι αποθηκευμένα τα δεδομένα ή τον τρόπο προγραμματισμού των λειτουργιών σ αυτά. Οι πιο συνηθισμένες λειτουργίες πάνω στις δομές δεδομένων είναι οι παρακάτω: Προσπέλαση (access), για την ανάγνωση ενός ή περισσοτέρων δεδομένων της δομής. Εισαγωγή (insertion), για την εισαγωγή ενός ή περισσοτέρων δεδομένων στην δομή. 15
Διαγραφή (deletion), για την διαγραφή ενός ή περισσοτέρων δεδομένων από την δομή. Αντιγραφή (copying), για την αντιγραφή ενός ή περισσοτέρων δεδομένων από την δομή σε μία άλλη δομή. Αναζήτηση (searching), για την εύρεση ενός ή περισσοτέρων δεδομένων της δομής. Ταξινόμηση (sorting), για την ταξινόμηση των δεδομένων της δομής με βάση κάποιο κριτήριο από τον χρήστη Συγχώνευση (merging), για την συγχώνευση των δεδομένων δύο ή περισσότερων δομών. Οι δομές δεδομένων χωρίζονται σε δύο μεγάλες κατηγορίες ανάλογα με τον τρόπο δέσμευσης της μνήμης που απαιτείται για την αποθήκευση των δεδομένων τους, τις στατικές και τις δυναμικές. Οι στατικές δομές δεδομένων γνωρίζουν το ακριβές μέγεθος των δεδομένων τους και δεσμεύουν εξ αρχής τον απαιτούμενο χώρο μνήμης γι αυτά. Τα δεδομένα αποθηκεύονται με τον τρόπο αυτό σε συνεχόμενες θέσεις μνήμης. Αντίθετα, οι δυναμικές δομές δεδομένων δεν έχουν σταθερό μέγεθος. Έτσι κάθε φορά που υπάρχει νέα εγγραφή δεδομένο δεσμεύεται στην μνήμη ο απαραίτητος χώρος. Η δέσμευση της μνήμης επομένως γίνεται με την τεχνικής της δυναμικής παραχώρησης μνήμης. Τα δεδομένα δεν βρίσκονται επομένως σε συνεχόμενες θέσεις μνήμης. 1.4.1 Βασικά παραδείγματα δομών δεδομένων Στη συνέχεια της ενότητας αυτής θα περιγραφούν οι βασικές δομές δεδομένων που χρησιμοποιούνται κατά κόρον από τους προγραμματιστές ενώ στη συνέχεια θα δοθούν αλγόριθμοι επίλυσης των λειτουργιών αναζήτησης και ταξινόμησης. Πίνακες (Arrays) Ο πίνακας είναι η πιο απλή και χρησιμοποιούμενη δομή δεδομένων. Είναι συνήθως στατική δομή αφού κατά τον ορισμό του σε ένα πρόγραμμα ορίζεται και το μέγεθος του. Ένας πίνακας μπορεί να είναι μιας διάστασης ή περισσοτέρων. Σε ένα πίνακα μπορεί να περιέχονται δεδομένα του ίδιου τύπου. Στοίβα (Stack) Η στοίβα είναι μία δομή δεδομένων στην οποία τα δεδομένα εισάγονται το ένα μετά το άλλο ενώ ο χρήστης έχει τη δυνατότητα να προσπελάσει μόνο το τελευταίο που έχει εισαχθεί ενώ το πρώτο στοιχείο που εισήχθη θα προσπελαστεί τελευταίο. Είναι μία δομή LIFO (Last In First Out). Η υλοποίηση μίας στοίβας μπορεί να γίνει με απλό τρόπο χρησιμοποιώντας έναν μονοδιάστατο πίνακα χρησιμοποιώντας πάντοτε έναν δείκτη που να δείχνει το πρώτο στοιχείο της στοίβας που είναι και το τελευταίο που εισήχθη σ αυτήν. Οι βασικές πράξεις σε μία στοίβα είναι: Empty (), η οποία επιστρέφει 1 αν η στοίβα είναι άδεια ή μηδέν στην αντίθετη περίπτωση. 16
Push (a), η οποία τοποθετεί το στοιχείο a στην κορυφή της στοίβας Top (), προσπελαύνει το στοιχείο που βρίσκεται στην κορυφή της στοίβας. Pop (), η οποία προσπελαύνει και παράλληλα διαγράφει το στοιχείο που βρίσκεται στην κορυφή της στοίβας. Ουρά (Queue) Η ουρά είναι μία δομή δεδομένων στην οποία τα δεδομένα εισάγονται το ένα μετά το άλλο ενώ ο χρήστης έχει τη δυνατότητα να προσπελάσει τα δεδομένα με την αντίστοιχη φορά που τα έχει εισάγει. Αποτελεί ένα τυπικό παράδειγμα μιας ουράς ανθρώπων σε μια τράπεζα. Είναι μία δομή FIFO (First In First Out). Η υλοποίηση μίας ουράς μπορεί να γίνει με απλό τρόπο χρησιμοποιώντας έναν μονοδιάστατο πίνακα χρησιμοποιώντας πάντοτε 2 δείκτες: έναν δείκτη που να δείχνει στο πρώτο στοιχείο της ουράς και έναν στο τελευταίο. Οι βασικές πράξεις σε μία ουρά είναι: Enqueue (a), η οποία τοποθετεί το στοιχείο a στο τέλος της ουράς Enqueue (), προσπελαύνει και παράλληλα διαγράφει το στοιχείο που βρίσκεται στην κορυφή της ουράς. Γραμμικές Λίστες (Linear Lists) Στις γραμμικές λίστες τα δεδομένα ως εγγραφές βρίσκονται σε οποιαδήποτε θέση ορίζοντας τους κόμβους της λίστας και η σύνδεση μεταξύ τους πραγματοποιείται με δείκτες. Από κάθε κόμβο ένας δείκτης δείχνει στον επόμενο κ.ο.κ. Οι βασικές λειτουργίες σε μία λίστα είναι οι παρακάτω: First (), η οποία επιστρέφει τον δείκτη προς τον πρώτο κόμβο Last (), η οποία επιστρέφει τον δείκτη προς τον τελευταίο κόμβο Insert (x,y), Ενθέτει τον κόμβο x προ του κόμβου y Delete (x), διαγράφει τον κόμβο x. Δέντρα (Trees) Αντίθετα με τις λίστες, στα δέντρα από κάθε κόμβο δεν ξεκινά ένας μόνο δείκτης που να δείχνει μόνο σε ένα κόμβο αλλά περισσότεροι οι οποίοι δείχνουν σε κόμβους που ονομάζονται απόγονοι του αρχικού. Ο πρώτος κόμβος στο δέντρο ονομάζεται ρίζα ενώ οι κόμβοι που δεν δείχνουν σε απόγονους ονομάζονται φύλλα. Τα δέντρα διακρίνονται σε 3 κατηγορίες: τα στατικά δέντρα όταν αυτά δεν επιδέχονται καμιά αλλαγή στην μορφή τους τα ημι-δυναμικά που παρέχουν κάποιες λειτουργίες τροποποίησης και τα δυναμικά που δίνουν τη δυνατότητα μέσω των πράξεων σ αυτά να μεταβάλλονται οι κόμβοι του δέντρου. 17
Ουρά προτεραιότητας (Priority queue) Στην δομή αυτή κυρίαρχο ρόλο έχει η προτεραιότητα που επισυνάπτεται σε κάθε δεδομένο της. Η προτεραιότητα αυτή ουσιαστικά είναι ένας αριθμός ο οποίος διατάσσει τα δεδομένα. Γράφοι (graphs) Η δομή αυτή δομείται με παρόμοιο τρόπο με τις προηγούμενες από κόμβους δεδομένων στους οποίους όμως δεν υπάρχει κάποια ιεραρχική οργάνωση. Οι κόμβοι αυτοί ενώνονται τυχαία μεταξύ τους με ένα σύνολο γραμμών. Ερωτήσεις- Ασκήσεις Κεφαλαίου 1. Τι είναι ο αλγόριθμος; Ποια κριτήρια πρέπει να ικανοποιεί; 2. Με ποιους τρόπους αναπαρίστανται οι αλγόριθμοι; 3. Δώστε 2 μονάδες μέτρησης της απόδοσης ενός αλγορίθμου. 4. Ποιοι είναι οι βασικοί τύποι εντολών ενός αλγορίθμου; 5. Περιγράψτε τον αλγόριθμο του Ευκλείδη. 6. Τι είναι η δομή δεδομένων; Ποια η διαφορά της στατικής με την δυναμική δομή δεδομένων. 7. Δώστε τις βασικές πράξεις πάνω στα στοιχεία μιας δομής δεδομένων. 18
ΚΕΦΑΛΑΙΟ 2. ΓΡΑΜΜΙΚΕΣ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ Σκοπός του κεφαλαίου αυτού είναι να παρουσιαστούν αναλυτικά οι πιο βασικές δομές δεδομένων που χρησιμοποιούνται τόσο στην κύρια όσο και στην δευτερεύουσα μνήμη. Αρχικά περιγράφονται οι γραμμικές δομές που χρησιμοποιούνται στην κύρια μνήμη δηλαδή οι πίνακες, η στοίβα, η ουρά και οι λίστες. Στις δομές αυτές ορίζεται μία γραμμική σχέση διάταξης για δύο οποιαδήποτε διαδοχικά στοιχεία τους. Ουσιαστικά οι δομές αυτές είναι μονοδιάστατες. Στη συνέχεια περιγράφονται οι μη γραμμικές δομές όπως τα δέντρα και οι γράφοι. Οι σχέσεις των δεδομένων μεταξύ τους σ αυτές τις μη γραμμικές δομές είναι σύνθετες. Σε κάθε μία από τις δομές αναλύεται η δομή τους, η λειτουργία τους καθώς και οι πράξεις που εκτελούνται ενώ δίνεται ο αλγόριθμος για πιο βασικές λειτουργίες. 2.1 Πίνακες Ο πίνακας (array) είναι η πιο απλή και χρησιμοποιούμενη δομή δεδομένων. Οι πίνακες υποστηρίζονται σε όλες τις γλώσσες προγραμματισμού και αποτελούν τη πιο συνηθισμένη δομή δεδομένων στον προγραμματισμό. Είναι συνήθως στατική δομή αφού κατά τον ορισμό του σε ένα πρόγραμμα ορίζεται και το μέγεθος του. Καταρχήν ένας πίνακας μπορεί να είναι μιας διάστασης ή περισσοτέρων. Παρακάτω δίνεται η εποπτική παράσταση ενός πίνακα μιας διάστασης και ενός 2 διαστάσεων. 1 2 3... N Μονοδιάστατος πίνακας Ν στοιχείων 1 2 1 2 3... N 3... Δύο διαστάσεων πίνακας Ν x Ν στοιχείων N Σε ένα πίνακα μπορεί να περιέχονται δεδομένα του ίδιου τύπου. Είναι σημαντικό ο προγραμματιστής να κατανοεί κάθε φορά το χώρο μνήμης που καταλαμβάνουν οι δομές που υλοποιεί και στην συγκεκριμένη περίπτωση ένας πίνακας. Τα στοιχεία των πινάκων αποθηκεύονται σε γειτονικές θέσεις μνήμης. Κάθε στοιχείο του πίνακα καταλαμβάνει το ίδιο χώρο μνήμης. Αν έχουμε ένα μονοδιάστατο πίνακα Ν στοιχείων και κάθε στοιχείο καταλαμβάνει μία λέξη μνήμης τότε όταν δηλώνεται ο 19
πίνακας σε μία γλώσσα προγραμματισμού όπως η C τότε δεσμεύονται εξαρχής Ν διαδοχικές θέσεις μνήμης για τον πίνακα. Στη γενική περίπτωση κάθε στοιχείο ενός πίνακα καταλαμβάνει τόσες θέσεις μνήμης ανάλογα με τον τύπο του και με την αρχιτεκτονική της κεντρικής μνήμης του υπολογιστή. Έτσι σε υπολογιστές όπου η λέξη μνήμης είναι ισοδύναμη με το byte τότε : Aν ο πίνακας περιέχει προσημασμένους ή μη ακεραίους αριθμούς τότε κάθε στοιχείο του καταλαμβάνει 1 byte και επομένως μία λέξη μνήμης. Αν ο πίνακας περιέχει ακεραίους αριθμούς τότε κάθε στοιχείο του καταλαμβάνει 2 bytes και επομένως δύο λέξεις μνήμης. Αν ο πίνακας περιέχει μεγάλους ακεραίους ή αριθμούς κινητής υποδιαστολής απλής ακρίβειας τότε κάθε στοιχείο του καταλαμβάνει 4 bytes και επομένως 4 λέξεις μνήμης. Αν ο πίνακας περιέχει αριθμούς κινητής υποδιαστολής διπλής ακρίβειας τότε κάθε στοιχείο του καταλαμβάνει 8 bytes και επομένως 8 λέξεις μνήμης. Παρακάτω δίνονται μερικά παραδείγματα πινάκων σε γλώσσα C. int num[100]. Στην περίπτωση αυτή ορίζεται ένας μονοδιάστατος πίνακας ο οποίος περιέχει 100 ακέραιους διαδοχικούς αριθμούς. char sympols[20]. Στην περίπτωση αυτή ορίζεται ένας μονοδιάστατος πίνακας ο οποίος περιέχει μία σειρά 20 χαρακτήρων. float num[100][100]. Στην περίπτωση αυτή ορίζεται ένας did;i;astatow πίνακας 100 x 100 ο οποίος περιέχει συνολικά 10000 πραγματικούς αριθμούς. 2.1.1 Μερικοί τύποι πινάκων Στην παράγραφο αυτή θα παρουσιάσουμε μερικούς τύπους πινάκων που χρησιμοποιούνται συχνά στην επίλυση προβλημάτων. 1. Συμμετρικοί πίνακες. Ένα πίνακας είναι συμμετρικός όταν είναι δύο διαστάσεων και τα στοιχεία του Α ij ικανοποιούν την σχέση Α ij = A ji για κάθε i και j. Π.χ. έστω έχουμε τον παρακάτω συμμετρικό πίνακα. 7 9 2 5 6 9 7 2 8 1 2 2 6 3 2 5 8 3 5 3 6 1 2 3 4 2. Τριγωνικοί πίνακες. Ένα πίνακας είναι τριγωνικός όταν είναι δύο διαστάσεων και τα στοιχεία του Α ij ικανοποιούν την σχέση Α ij = 0 για i > j. Π.χ. έστω έχουμε τον παρακάτω τριγωνικό πίνακα. 7 0 0 0 0 20
9 7 0 0 0 2 2 6 0 0 5 8 3 5 0 6 1 2 3 4 3. Τριδιαγώνιοι πίνακες. Ένα πίνακας είναι τριδιαγώνιος όταν είναι δύο διαστάσεων και όταν τα μόνα μη μηδενικά στοιχεία του Α ij είναι αυτά που ικανοποιούν την σχέσεις i = j ή i-j =1 ή j i = 1. Στην περίπτωση αυτή τα μόνα μη μηδενικά στοιχεία του πίνακα είναι αυτά της διαγωνίου του καθώς και τα γειτονικά της. Π.χ. έστω έχουμε τον παρακάτω τριδιαγώνιο πίνακα. 7 9 2 0 0 9 7 2 8 0 2 2 6 3 2 0 8 3 5 3 0 0 2 3 4 4. Αραιοί πίνακες. Ένα πίνακας είναι αραιός όταν πάρα πολλά στοιχεία του είναι 0. Π.χ. έστω έχουμε τον παρακάτω αραιό πίνακα. 0 0 1 0 1 0 7 0 2 0 2 0 0 0 0 1 0 3 0 0 0 0 0 3 0 2.1.2 Συμβολοσειρές Μία πολύ σημαντική κατηγορία μονοδιάστατων πινάκων είναι αυτοί στους οποίους τα στοιχεία τους είναι χαρακτήρες και ονομάζονται συμβολοσειρές (strings). Ουσιαστικά πρόκειται για αλφαριθμητικούς πίνακες. Λόγω της σπουδαιότητας τους και της ευρείας τους χρήσης σε εφαρμογές κάθε γλώσσα προγραμματισμού προσφέρει ένα σύνολο συναρτήσεων οι οποίοι έχουν σκοπό να εκτελέσουν συγκεκριμένες λειτουργίες πάνω σε συμβολοσειρές όπως συνέλιξη συμβολοσειρών, αποκοπή στοιχείων ή και τμημάτων της συμβολοσειράς κ.α. Βασικό στοιχείο που πρέπει να τονιστεί είναι το γεγονός ότι κάθε στοιχείου του πίνακα μπορεί να είναι χαρακτήρας αλλά και από μόνο του μία συμβολοσειρά. Στην περίπτωση αυτή μπορεί η συμβολοσειρά να είναι σταθερού ή μεταβλητού μεγέθους. Στην πρώτη περίπτωση κατά τον ορισμό του πίνακα δεσμεύεται ο συνολικός απαραίτητος χώρος μνήμης για την αποθήκευση των στοιχείων του. Δηλαδή αν ο 21
πίνακας είναι ΝxΝ τότε στην κάθε γραμμή δεσμεύεται χώρος για συμβολοσειρά Ν χαρακτήρων ανεξάρτητα από το πραγματικό μέγεθος της. Αυτό έχει σαν αποτέλεσμα να σπαταλείται αρκετός χώρος. Στην δεύτερη περίπτωση συμβολοσειρών μεταβλητού μεγέθους σε κάθε γραμμή δεν δεσμεύεται χώρος Ν χαρακτήρων αλλά μόνο 2 στοιχείων. Το πρώτο περιέχει τον αριθμό του πλήθους των χαρακτήρων της συμβολοσειράς της συγκεκριμένης γραμμής δηλαδή το μήκος της ενώ το δεύτερο στοιχείο είναι ένας δείκτης που δείχνει στην διεύθυνση μνήμης που περιέχεται ο πρώτος χαρακτήρας της συμβολοσειράς. Έτσι το πρόγραμμα κατά την εκτέλεση του όταν θέλει έστω την πρώτη συμβολοσειρά του πίνακα πηγαίνει στην διεύθυνση που μνήμης που είναι καταχωρημένος ο πρώτος χαρακτήρας και ανακτά στη συνέχεια το περιεχόμενο των συνεχόμενων θέσεων μνήμης όσον ορίζεται από το αριθμό που δίνει το μήκος της συμβολοσειράς. 2.2 Στοίβες Μία ακόμα γραμμική δομή δεδομένων είναι η στοίβα (stack) στην οποία τα δεδομένα της αποθηκεύονται διαδοχικά, το ένα μετά το άλλο. Όταν ο χρήστης εισάγει ένα στοιχείο αυτό αποθηκεύεται στο τέλος της στοίβας. Όταν όμως θέλει να εξάγει ένα στοιχείο της στοίβας τότε έχει τη δυνατότητα να προσπελάσει μόνο το τελευταίο στοιχείο που έχει εισαχθεί. Αυτό σημαίνει ότι το πρώτο στοιχείο που εισήχθη στη στοίβα θα προσπελαστεί τελευταίο. Η λειτουργία της αυτή την κατατάσσει στις δομές LIFO (Last In First Out). Συχνά στην καθημερινή μας ζωή συναντάμε περιπτώσεις ή προβλήματα που απαιτούν την εφαρμογή δομής στοίβας για τον ορισμό και την επίλυση τους. Μία στοίβα πιάτων είναι χαρακτηριστικό παράδειγμα. Πρέπει να τονιστεί παράλληλα ότι η στοίβα αποτελεί μία βασική δομή δεδομένων η οποία χρησιμοποιείται συχνά στην επιστήμη των υπολογιστών. Έτσι όταν στον προγραμματισμό καλείται μία συνάρτηση και στην συνέχεια μία άλλη κ.ο.κ. τότε η διαδρομή αυτή «κρατείται» σε μία στοίβα ώστε το πρόγραμμα να εκτελεστεί επιτυχώς. Ποιες λειτουργίες όμως εφαρμόζονται σε μία στοίβα; Έστω έχουμε μία στοίβα στην οποία έχουμε αποθηκευμένα τα στοιχεία X, Y, Z και V όπως φαίνεται στο παρακάτω σχήμα. Εισαγωγή στοιχείων Εξαγωγή στοιχείων Στη στοίβα αυτή υπάρχουν δύο βασικές επιλογές λειτουργιών. Ένας χρήστης μπορεί να ενθέσει ένα στοιχείο στη στοίβα, έστω P και εκτελείται η πράξη push(p). Τότε η στοίβα αποκτά την ακόλουθη μορφή: V Z Y X 22
P V Z Y X Επίσης ένας χρήστης μπορεί να απωθήσει ένα στοιχείο από τη στοίβα και εκτελείται η πράξη pop(). Ουσιαστικά η πράξη pop προσπελαύνει το τελευταίο στοιχείο της στοίβας και στη συνέχεια το σβήνει από τη στοίβα. Τότε η στοίβα αποκτά την ακόλουθη μορφή: Z Y X Υπάρχουν βέβαια και άλλες πράξεις που εφαρμόζονται σε μία στοίβα, όχι βέβαια τόσο σημαντικές όσο οι προηγούμενες δύο που αναφέρθηκαν. Ειδικότερα, οι βασικές πράξεις σε μία στοίβα είναι: Push (a), η οποία τοποθετεί το στοιχείο a στην κορυφή της στοίβας Pop (), η οποία προσπελαύνει και παράλληλα διαγράφει το στοιχείο που βρίσκεται στην κορυφή της στοίβας. Top (), προσπελαύνει το στοιχείο που βρίσκεται στην κορυφή της στοίβας. Empty (), η οποία επιστρέφει 1 αν η στοίβα είναι άδεια ή μηδέν στην αντίθετη περίπτωση. 2.2.1 Αλγόριθμος υλοποίησης στοίβας Η υλοποίηση μίας στοίβας ουσιαστικά σημαίνει τον ορισμό της μνήμης που δεσμεύεται για αυτήν καθώς και την υλοποίηση των πράξεων της. Μία στοίβα μπορεί να υλοποιηθεί ως ένας πίνακας ορισμένου ή μη μεγέθους ή μιας απλής λίστας. Αυτό σημαίνει ότι η στοίβα μπορεί να είναι τόσο μία στατική δομή όσο και μία δυναμική ανάλογα τον τρόπο υλοποίησης της. Η πιο συνηθισμένη μορφή μιας στοίβας είναι ένας πίνακας περιορισμένου μεγέθους. Η στοίβα επομένως μπορεί να υλοποιηθεί με απλό τρόπο χρησιμοποιώντας 23
έναν μονοδιάστατο πίνακα μεγέθους έστω Array[N] χρησιμοποιώντας πάντοτε έναν δείκτη που να δείχνει το πρώτο στοιχείο της στοίβας που είναι και το τελευταίο που εισήχθη σ αυτήν και ονομάζεται κεφαλή της στοίβας (μεταβλητή Head). Κατά την υλοποίηση των πράξεων της στοίβας χρησιμοποιείτε πάντοτε μία μεταβλητή τύπου Boolean η οποία ενημερώνει πάντοτε μετά την αίτηση εκτέλεσης πράξης αν αυτήν εκτελέστηκε επιτυχώς ή όχι (έστω μεταβλητή check). Πράξη Push Αν θεωρήσουμε ότι στοίβα θα περιέχει ακεραίους αριθμούς (ο πίνακας Array[N] δηλώνεται ως ακέραιος) τότε η πράξη ένθεσης καλείται όταν θέλουμε να εισάγουμε ένα στοιχείο στη στοίβα έστω num (μεταβλητή num τύπου ακεραίου). Η Push είναι μία ανεξάρτητη διαδικασία η οποία καλείται από το κύριο πρόγραμμα όταν θέλουμε να εισάγουμε ένα στοιχείο στη στοίβα. Συντάσσεται επομένως ως μία συνάρτηση στην περίπτωση μας τύπου ακεραίου στην οποία μεταφέρεται μαζί ως αρχικά δεδομένα και η τιμή του στοιχείου num. Όταν καλείται η push επομένως έχει την μορφή push(num). Όταν γίνει αίτηση για ένθεση (push(num)) πρέπει να εξεταστεί αρχικά το γεγονός αν υπάρχει διαθέσιμος χώρος στη στοίβα για να εισαχθεί το στοιχείο num. Τονίσαμε ότι η μεταβλητή Head δείχνει στο τελευταίο στοιχείο της στοίβας (κεφαλή της). Αν επομένως στο παράδειγμα μας, η κεφαλή δείχνει στο στοιχείο N τότε δεν υπάρχει χώρος στη στοίβα. Άρα δεν εκτελείται η πράξη και αυτό δηλώνεται ορίζοντας την μεταβλητή check ως ψευδής. Στην αντίθετη περίπτωση υπάρχει χώρος για το στοιχείο num και τότε η μεταβλητή head αυξάνεται κατά ένα ώστε να δείχνει στην επόμενη θέση της στοίβας που είναι κενή. Στη συνέχεια το στοιχείο Num εισάγεται στη θέση που δείχνει η head. Η ένθεση εκτελείται επιτυχώς και ορίζεται η μεταβλητή check ως αληθής. Παρακάτω δίνεται ο αλγόριθμος υλοποίησης της πράξης push σε ψευδοκώδικα. Αλγόριθμος Push Δεδομένα //num : ακέραιος αριθμός// Αρχή Aν Head<N τότε Head= Head+1; Array[Head]=num; Check=αληθής; Αλλιώς Check=ψευδής; Τέλος Αν Αποτελέσματα // Check // Τέλος Push Πράξη Pop Αν θεωρήσουμε ότι στοίβα θα περιέχει ακεραίους αριθμούς (ο πίνακας Array[N] δηλώνεται ως ακέραιος) τότε η πράξη απώθησης καλείται όταν θέλουμε να 24
διαβάσουμε και να σβήσουμε παράλληλα το στοιχείο κεφαλή της στοίβας έστω num (μεταβλητή num τύπου ακεραίου). Η Pop είναι μία ανεξάρτητη διαδικασία και συντάσσεται ως μία συνάρτηση στην περίπτωση μας τύπου ακεραίου. Όταν καλείται η pop επομένως έχει την μορφή push() και επιστρέφει την κεφαλή της στοίβας (αν εκτελεστεί επιτυχώς). Όταν γίνει αίτηση για απώθηση (pop()) πρέπει να εξεταστεί αρχικά το γεγονός αν υπάρχουν στοιχεία της στοίβας.. Τονίσαμε ότι η μεταβλητή Head δείχνει στο τελευταίο στοιχείο της στοίβας (κεφαλή της). Αν επομένως η head έχει την τιμή 0 σημαίνει ότι η στοίβα είναι κενή και δεν εκτελείται η πράξη. Επομένως επιστρέφεται η μεταβλητή check ως ψευδής. Στην αντίθετη περίπτωση υπάρχουν στοιχεία στη στοίβα και η pop επιστρέφει την κεφαλή της που είναι το στοιχείο Array[Head]. Η μεταβλητή head μειώνεται κατά ένα ώστε να δείχνει στο προηγούμενο στοιχείο της στοίβας που θα αποτελεί στη συνέχεια και την κεφαλή της. Η απώθηση εκτελείται επιτυχώς και ορίζεται η μεταβλητή check ως αληθής. Παρακάτω δίνεται ο αλγόριθμος υλοποίησης της πράξης pop σε ψευδοκώδικα. Αλγόριθμος Pop Αρχή Aν Head>0 τότε Num=Array[Head] Head= Head-1; check=αληθής; Αλλιώς Check=ψευδής; Τέλος Αν Αποτελέσματα // Check, num// Τέλος Pop 2.2.2 Εφαρμογές Στοίβας Πολωνικός συμβολισμός Σε πολλές περιπτώσεις ιδιαίτερα στην επιστήμη των υπολογιστών απαιτείται η χρήση ενός συμβολισμού για τις αριθμητικές και λογικές παραστάσεις στον οποίο δεν χρησιμοποιούνται παρενθέσεις. Για το λόγο αυτό ο πολωνός μαθηματικός, το 1951, Jan Lukasiewicz παρουσίασε έναν συμβολισμό ο οποίος προς τιμή του ονομάστηκε πολωνικός. Οι παραστάσεις τις οποίες χρησιμοποιεί ο άνθρωπος αποτελούνται από τους τελεστέους, τους τελεστές καθώς και από παρενθέσεις. Η συνηθισμένη τους μορφή όπως την γνωρίζουμε είναι οι τελεστές να παρεμβάλλονται ανάμεσα στους τελεστέους. Η μορφή αυτή των παραστάσεων ονομάζεται ένθετη (infix). Σύμφωνα με τον πολωνικό συμβολισμό κάθε ένθετη μορφή μπορεί να μετατραπεί στην προθεματική στην οποία οι τελεστές προηγούνται από τους τελεστέους αλλά και στην μεταθετική στην οποία οι τελεστές ακολουθούν τους τελεστέους. Έτσι για παράδειγμα έχουμε: Ένθετη Προθεματική Μεταθετική 25
1 α+β +αβ αβ+ 2 α+β*γ +α*βγ αβγ*+ 3 α*β*γ *α*βγ αβ*γ* 4 α*β+γ*δ +*αβ*γδ αβ*γδ*+ Ένα αρχικό πρόβλημα που παρουσιάστηκε είναι με την πράξη της αφαίρεσης. Το σύμβολο μπορεί να θεωρηθεί σε μία παράσταση είτε ως πρόσημο είτε ως το σύμβολο της πράξης αφαίρεσης. Στην περίπτωση του πολωνικού συμβολισμού δεν έχουμε πράξη αφαίρεσης. Αυτή μετατρέπετε σε πράξη πρόσθεσης ενός αριθμού με τον αντίθετο του επόμενου. Ειδικότερα όταν ζητείται η εκτέλεση της πράξης α-β τότε αυτή μετατρέπεται στην πράξη α+(-β). Στην συνέχεια στον πολωνικό συμβολισμό αφαιρούνται και οι παρενθέσεις όπως θα δούμε παρακάτω. Επίσης πρέπει να τονιστεί ότι βασικό στοιχείο αποτελεί ο ορισμός του πλήθους των τελεστέων που αντιστοιχούν σε ένα τελεστή. Παράλληλα είναι σημαντικό να γνωρίζουμε ότι τόσο στην ένθετη μορφή των παραστάσεων όσο και στη μεταθετική και προθεματική ισχύει μία ιεραρχία ή προτεραιότητα στις πράξεις. Αυτή η ιεραρχία στις βασικές πράξεις είναι : 1. Ύψωση σε δύναμη. 2. Πολλαπλασιασμός και διαίρεση. 3. Πρόσθεση και αφαίρεση. Οι εφαρμογές που έχει ο πολωνικός συμβολισμός στην επιστήμη των υπολογιστών είναι αρκετές. Στις περισσότερες γλώσσες προγραμματισμού μπορεί ο προγραμματιστής να εισάγει εντολές για την εκτέλεση παραστάσεων στην ένθετη μορφή όμως ο μεταγλωτιστής της γλώσσας αρχικά μετατρέπει την παράσταση στην προθεματική ή την μεταθετική μορφή και στη συνέχεια εκτελεί τις πράξεις. Επίσης ανάλογη διαδικασία εκτελείται και σε διάφορους τύπους προγραμματιζόμενων αριθμομηχανών κ.α.. Ο πιο συνηθισμένος συμβολισμός παραστάσεων στις γλώσσες προγραμματισμού είναι της μεταθετικής μορφής. Παρακάτω θα εξεταστεί ο τρόπος με τον οποίο μετατρέπεται η ένθεση μορφή μιας παράστασης είτε με παρενθέσεις είτε όχι στην αντίστοιχη μεταθετική. Επίσης θα εξεταστεί ο τρόπος εκτέλεσης πράξεων με δοσμένη την παράσταση στην μεταθετική μορφή. Σε όλες τις παραπάνω περιπτώσεις χρησιμοποιούνται στοίβες. Μετατροπή ένθετης παράστασης χωρίς παρενθέσεις σε μεταθετική Για την μετατροπή μιας παραστάσεις από την ένθετη μορφή στην μεταθετική ακολουθούμε τα παρακάτω βήματα. 1. Αρχικά κατατάσσουμε τα σύμβολα της ένθετης παράστασης το ένα μετά το άλλο ώστε να μπορούν να αριθμηθούν. 2. Στη συνέχεια εξετάζονται ένα ένα διαδοχικά όλα τα σύμβολα της παράστασης ξεκινώντας από αυτό αριστερά της. 3. Όλοι οι τελεστέοι τοποθετούνται άμεσα ο ένας στα δεξιά του άλλου σε μία παράσταση σε μεταθετική μορφή. 26
4. Όλοι οι τελεστές ωθούνται σε μία στοίβα ενδιάμεσα και στη συνέχεια απωθούνται και ενσωματώνονται στα δεξιά της παράστασης μεταθετικής μορφής που σχηματίζεται σταδιακά. Ειδικότερα, για τον παραπάνω λόγο ισχύουν οι παρακάτω κανόνες: Α. Αν ο τελεστής που εξετάζουμε έχει μεγαλύτερη ιεραρχία από αυτόν που βρίσκεται στην κεφαλή της στοίβας τότε αυτός εισάγεται στην στοίβα (push). B. Αν ο τελεστής που εξετάζουμε έχει μικρότερη ιεραρχία από αυτόν που βρίσκεται στην κεφαλή της στοίβας τότε αρχικά απωθούνται (pop) όλοι οι τελεστές της στοίβας οι οποίοι έχουν μεγαλύτερη ή ίση ιεραρχία από αυτόν. Οι τελεστές αυτοί εισάγονται διαδοχικά στα δεξιά της παράστασης της μεταθετικής μορφής. Στη συνέχεια ο τελεστής που εξετάζουμε εισάγεται στην στοίβα (push) Για την καλύτερη κατανόηση των παραπάνω κανόνων δίνεται το παρακάτω παράδειγμα μετατροπής της αριθμητικής παράστασης ένθετης μορφής : στην αντίστοιχη μεταθετική του. α+β*γ ε +δ/ζ Αρχικά διατάσσουμε τα σύμβολα της παράστασης και έχουμε: α+β*γ^ε+δ/ζ Εξετάζουμε τα σύμβολα της παράστασης διαδοχικά ξεκινώντας από τα αριστερά και έχουμε: Σύμβολα στην ένθετη μορφή Στοίβα α - α + + α β + αβ * *+ αβ γ *+ αβγ ^ ^*+ αβγ ε ^*+ αβγε Μεταθετική μορφή + + αβγε^*+ δ + αβγε^*+δ / /+ αβγε^*+δ ζ /+ αβγε^*+δζ - αβγε^*+δζ/+ Μετατροπή ένθετης παράστασης με παρενθέσεις σε μεταθετική Στην περίπτωση που η παράσταση μας στην ένθετη μορφή έχει παρενθέσεις τότε ισχύουν οι παραπάνω κανόνες μαζί με τους παρακάτω δύο: 27
1. Όταν κατά την διαδοχική εξέταση των συμβόλων της παράστασης συναντάται αριστερή παρένθεση τότε αυτήν εισάγεται ως έχει στην στοίβα. 2. Όταν κατά την διαδοχική εξέταση των συμβόλων της παράστασης συναντάται δεξιά παρένθεση τότε αυτή προκαλεί την απώθηση από τη στοίβα όλων των τελεστών μέχρι να συναντήσουμε την αριστερή παρένθεση. Οι παρενθέσεις εξαλείφονται. Έστω έχουμε την παράσταση στην ένθετη μορφή: (a+b)*(c-d)^e*f. Αρχικά μετατρέπουμε την πράξη αφαίρεσης σε πρόσθεση και έχουμε : (a+b)*(c+-d)^e*f. Εξετάζουμε τα σύμβολα της παράστασης διαδοχικά ξεκινώντας από τα αριστερά και έχουμε: Σύμβολα στην ένθετη μορφή Στοίβα ( ( a ( a + +( a b +( ab ) - ab+ * * ab+ ( (* ab+ c (* ab+c + +(* ab+c -d +(* ab+c-d Μεταθετική μορφή ) * ab+c-d+ ^ ^* ab+c-d+ e ^* ab+c-d+e * * ab+c-d+e^* f * ab+c-d+e^*f - - ab+c-d+e^*f* Υπολογισμός παραστάσεις μεταθετικής μορφής Αφού η παράσταση ένθετης μορφής μετατραπεί στην αντίστοιχη μεταθετική στη συνέχεια ο υπολογισμός της είναι εύκολος. Εξετάζουμε τα στοιχεία της παράστασης ένα ένα διαδοχικά από τα αριστερά. Όταν συναντάται τελεστέος τότε η αντίστοιχη τι9μή του εισάγεται στην στοίβα. Όταν συναντάται τελεστής τότε γίνεται η πράξη που δηλώνει ανάμεσα στην κεφαλή της στοίβας και το αμέσως επόμενο της στοίβας και το αποτέλεσμα αποτελεί την νέα κεφαλή της. Δηλαδή γίνονται δύο συνεχόμενα 28