1 Εισαγωγή στις οµές εδοµένων 2. 2 Στοίβα (Stack) 4. 3 Ουρά (Queue) 7. 4 Λίστα (List) 9. 5 Συνδεδεµένη Λίστα (Linked List) 14



Σχετικά έγγραφα
υαδικό έντρο Αναζήτησης (BSTree)

Περιεχόµενα. 1 Εισαγωγή στις οµές εδοµένων 3. 2 Στοίβα (Stack) 5

Ισοζυγισµένο έντρο (AVL Tree)

5 ΔΕΝΤΡΑ (Trees) Σχήµα 5.1 : ενδροειδής αναπαράσταση αρχείων στα Windows. έντρα. \ {root directory} Accessories. Program Files.

ΠΑΡΑΡΤΗΜΑ: QUIZ ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ

1 Εισαγωγή στις οµές εδοµένων 2. 2 Στοίβα (Stack) 4. 3 Ουρά (Queue) 7. 4 Λίστα (List) 9. 5 Συνδεδεµένη Λίστα (Linked List) 14

1 Εισαγωγή στις οµές εδοµένων 2. 2 Στοίβα (Stack) 4. 3 Ουρά (Queue) 7. 4 Λίστα (List) 9. 5 Συνδεδεµένη Λίστα (Linked List) 14

Oι βασικές πράξεις (λειτουργίες) που ορίζονται για τον τύπο στοίβα αναφέρονται παρακάτω:

Αλγόριθμοι και Δομές Δεδομένων (IΙ) (γράφοι και δένδρα)

AVL-trees C++ implementation

Διδάσκων: Παναγιώτης Ανδρέου

Διάλεξη 12: Δέντρα ΙΙ Δυαδικά Δέντρα

Οι ϐασικές πράξεις που ορίζονται για τη δοµή δεδοµένων σωρός, είναι η πράξη της εισαγωγής και η πράξη της διαγραφής ενός στοιχείου.

Δηµοσθένης Σταµάτης Τµήµα Πληροφορικής ΑΤΕΙ ΘΕΣΣΑΛΟΝΙΚΗΣ ΔΕΝΤΡΑ (TREES) B C D E F G H I J K L M

Πανεπιστήμιο Πειραιώς Σχολή Τεχνολογιών Πληροφορικής και Επικοινωνιών Τμήμα Ψηφιακών Συστημάτων ομές εδομένων

Δομές Δεδομένων & Ανάλυση Αλγορίθμων. 3ο Εξάμηνο. Ουρά (Queue) Υλοποίηση της με τη βοήθεια πίνακα.

Διδάσκων: Κωνσταντίνος Κώστα

Δομές Δεδομένων και Αλγόριθμοι

Διάλεξη 16: Σωροί. Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Ουρές Προτεραιότητας - Ο ΑΤΔ Σωρός, Υλοποίηση και πράξεις

Δυαδικά Δένδρα Αναζήτησης, Δένδρα AVL

Δομές Δεδομένων. Δημήτρης Μιχαήλ. Υλοποίηση Δυαδικού Σωρού σε γλώσσα Java. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Διάλεξη 16: Σωροί. Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Ουρές Προτεραιότητας - Ο ΑΤΔ Σωρός, Υλοποίηση και πράξεις

Διάλεξη 05: Αφηρημένοι Τύποι Δεδομένων

ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ. Βασικές Ιδιότητες και Διάσχιση Κεφάλαιο 5 ( και ) Ε. Μαρκάκης Επίκουρος Καθηγητής

ΕΠΛ231 Δομές Δεδομένων και Αλγόριθμοι 5. Αφηρημένοι Τύποι Δεδομένων / Στοίβες και Ουρές

Αφηρημένες Δομές Δεδομένων. Στοίβα (Stack) Υλοποίηση στοίβας

ιαφάνειες παρουσίασης #10 (β)

Βασικές Έννοιες Δοµών Δεδοµένων

Ενότητα 7 Ουρές Προτεραιότητας

Διάλεξη 22: Δυαδικά Δέντρα. Διδάσκων: Παναγιώτης Ανδρέου

Βασικές δοµές δεδοµένων. Ορολογία λιστών. 8.1 Βασικές έννοιες δοµών δεδοµένων 8.2 Υλοποίηση δοµών δεδοµένων 8.3 Μια σύντοµη υπόθεση εργασίας

ιαφάνειες παρουσίασης #11

Διάλεξη 17: Δυαδικά Δέντρα. Διδάσκων: Κωνσταντίνος Κώστα Διαφάνειες: Δημήτρης Ζεϊναλιπούρ

Συγκρίσιμα Αντικείμενα (comparable)

Δομές Δεδομένων. Δημήτρης Μιχαήλ. Δέντρα Αναζήτησης. Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

ΔυαδικάΔΕΝΔΡΑΑναζήτησης

Διάλεξη 17: O Αλγόριθμος Ταξινόμησης HeapSort

Δομές Δεδομένων & Αλγόριθμοι

Συλλογές, Στοίβες και Ουρές

ΚΕΦΑΛΑΙΟ 8: Αφαίρεση δεδοµένων

Πανεπιστήμιο Πειραιώς Σχολή Τεχνολογιών Πληροφορικής και Επικοινωνιών Τμήμα Ψηφιακών Συστημάτων ομές εδομένων

Πανεπιστήμιο Πειραιώς Σχολή Τεχνολογιών Πληροφορικής και Επικοινωνιών Τμήμα Ψηφιακών Συστημάτων ομές εδομένων

Δομές Δεδομένων (Data Structures)

ΑΛΓΟΡΙΘΜΟΙ ΜΕ C. ΝΙΚΟΛΑΟΣ ΣΑΜΑΡΑΣ Αναπληρωτής Καθηγητής. CMOR Lab. Computational Methodologies and Operations Research

ΕΠΛ 231 οµές εδοµένων και Αλγόριθµοι Άννα Φιλίππου,

ΠΛΗ111. Ανοιξη Μάθηµα 7 ο. έντρο. Τµήµα Ηλεκτρονικών Μηχανικών και Μηχανικών Υπολογιστών Πολυτεχνείο Κρήτης

ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΥΠΡΟΥ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

ΠΑΝΕΠΙΣΤΗΜΙΟ ΚΥΠΡΟΥ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ EPL035: ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ ΚΑΙ ΑΛΓΟΡΙΘΜΟΙ

2.2.5 ΑΝΑΠΑΡΑΣΤΑΣΗ ΑΛΓΟΡΙΘΜΟΥ

Δομές Δεδομένων & Αλγόριθμοι

Οι βασικές λειτουργίες (ή πράξεις) που γίνονται σε μια δομή δεδομένων είναι:

Δοµές Δεδοµένων. 6η Διάλεξη Αναδροµικές Εξισώσεις και Αφηρηµένοι Τύποι Δεδοµένων. Ε. Μαρκάκης

Αλγόριθµοι και Πολυπλοκότητα

Δένδρα. Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα:

1 ΕΙΣΑΓΩΓΗ. Πρωταρχικοί Τύποι

public class ArrayStack implements Stack {

Τύποι Δεδομένων και Απλές Δομές Δεδομένων. Παύλος Εφραιμίδης V1.0 ( )

ΑΝΑΠΤΥΞΗ ΕΦΑΡΜΟΓΩΝ ΣΕ ΠΡΟΓΡΑΜΜΑΤΙΣΤΙΚΟ ΠΕΡΙΒΑΛΛΟΝ

Διάλεξη 16: Σωροί. Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Ουρές Προτεραιότητας - Ο ΑΤΔ Σωρός, Υλοποίηση και πράξεις

οµές εδοµένων 3 ο Εξάµηνο ΕΝΟΤΗΤΑ 4 ΕΝ ΡΑ

Οντοκεντρικός Προγραμματισμός

Διάλεξη 26: Σωροί. Διδάσκων: Παναγιώτης Ανδρέου

Ουρά Προτεραιότητας: Heap

Κεφάλαιο 10 Ψηφιακά Λεξικά

Βασικές οµές εδοµένων

Δοµές Δεδοµένων. 11η Διάλεξη Ταξινόµηση Quicksort και Ιδιότητες Δέντρων. Ε. Μαρκάκης

Διάλεξη 11: Δέντρα Ι - Εισαγωγή σε Δενδρικές Δομές Δεδομένων

Γράφημα. Συνδυαστικό αντικείμενο που αποτελείται από 2 σύνολα: Σύνολο κορυφών (vertex set) Σύνολο ακμών (edge set) 4 5 πλήθος κορυφών πλήθος ακμών

Δομές Δεδομένων. Καθηγήτρια Μαρία Σατρατζέμη. Τμήμα Εφαρμοσμένης Πληροφορικής. Δομές Δεδομένων. Τμήμα Εφαρμοσμένης Πληροφορικής

Απλές Δοµές Δεδοµένων Στην ενότητα αυτή θα γνωρίσουµε ορισµένες απλές Δοµές Δεδοµένων και θα τις χρησιµοποιήσουµε για την αποδοτική επίλυση του προβλή

ΤΕΧΝΙΚΕΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΑΦΟΥΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΥ. Αντικείμενα με πίνακες. Constructors. Υλοποίηση Στοίβας

Συνδεδεμένη Λίστα ΣΛ null VK 23

υναµικές οµές εδοµένων

Δένδρα. Μαθηματικά (συνδυαστικά) αντικείμενα. Έχουν κεντρικό ρόλο στην επιστήμη των υπολογιστών :

Διάλεξη 13: Δέντρα ΙΙΙ - Ισοζυγισμένα Δέντρα, AVL Δέντρα

#2 Αλγόριθµοι, οµές εδοµένων και Πολυπλοκότητα

Άσκηση 1 (ανακοινώθηκε στις 20 Μαρτίου 2017, προθεσμία παράδοσης: 24 Απριλίου 2017, 12 τα μεσάνυχτα).

Ουρά Προτεραιότητας: Heap

Γράφηµα (Graph) Εργαστήριο 10. Εισαγωγή

ΟΙΚΟΝΟΜΙΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΘΗΝΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ. Δοµές Δεδοµένων

Διάλεξη 06: Συνδεδεμένες Λίστες & Εφαρμογές Στοιβών και Ουρών

Δομές Δεδομένων (Data Structures)

Σύνοψη Προηγούμενου (1/2) Στοίβες, Ουρές, Ουρές Προτεραιότητας. Σύνοψη Προηγούμενου (2/2) Σημερινό Μάθημα. Πίνακες. Εισαγωγή, σε χρόνο O(1).

Δομές Δεδομένων. Ενότητα 10: Πλήρη Δυαδικά Δέντρα, Μέγιστα/Ελάχιστα Δέντρα & Εισαγωγή στο Σωρό- Ο ΑΤΔ Μέγιστος Σωρός. Καθηγήτρια Μαρία Σατρατζέμη

Δομές Δεδομένων Ενότητα 5

Μάθημα 22: Δυαδικά δέντρα (Binary Trees)

Διάλεξη 08: Λίστες ΙΙ Κυκλικές Λίστες

ΑΤΕΙ ΘΕΣΣΑΛΟΝΙΚΗΣ. Δηµοσθένης Σταµάτης Τµήµα Πληροφορικής

Διάλεξη 11: Δέντρα Ι Εισαγωγή σε Δενδρικές Δομές Δεδομένων

Ενότητα 7 Ουρές Προτεραιότητας

2.1 Αντικειµενοστρεφής προγραµµατισµός

Initialize each person to be free. while (some man is free and hasn't proposed to every woman) { Choose such a man m w = 1 st woman on m's list to

ΗΥ360 Αρχεία και Βάσεις εδοµένων

Ανάπτυξη Μεγάλων Εφαρµογών στη Γλώσσα C (2)

Κατ οίκον Εργασία 3 Σκελετοί Λύσεων

Στοίβες - Ουρές. Στοίβα (stack) Γιάννης Θεοδωρίδης, Νίκος Πελέκης, Άγγελος Πικράκης Τµήµα Πληροφορικής

ΕΝΟΤΗΤΑ 5 ΥΝΑΜΙΚΑ ΛΕΞΙΚΑ ΙΣΟΖΥΓΙΣΜΕΝΑ ΕΝ ΡΑ

ΣΧΟΛΗ ΔΙΟΙΚΗΣΗΣ ΚΑΙ ΟΙΚΟΝΟΜΙΑΣ ΤΜΗΜΑ ΔΙΟΙΚΗΣΗΣ ΕΠΙΧΕΙΡΗΣΕΩΝ (ΠΑΤΡΑ) ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ

public class ArrayQueue implements Queue {

int array[10]; double arr[5]; char pin[20]; Προγραµµατισµός Ι

Transcript:

ΤΕΧΝΟΛΟΓΙΚΟ ΕΚΠΑΙΔΕΥΤΙΚΟ ΙΔΡΥΜΑ ΘΕΣΣΑΛΟΝΙΚΗΣ ΣΧΟΛΗ ΤΕΧΝΟΛΟΓΙΚΩΝ ΕΦΑΡΜΟΓΩΝ Τ Μ Η Μ Α Π Λ Η Ρ Ο Φ Ο Ρ Ι Κ Η Σ Δ Ο Μ Ε Σ Δ Ε Δ Ο Μ Ε Ν Ω Ν Α Κ Α Δ Η Μ Α Ι Κ Ο Ε Τ Ο Σ 2 0 0 9-2 0 1 0 Σ η μ ε ι ώ σ ε ι ς Ε ρ γ α σ τ η ρ ί ο υ Version 3.12

Περιεχόµενα 1 Εισαγωγή στις οµές εδοµένων 2 2 Στοίβα (Stack) 4 3 Ουρά (Queue) 7 4 Λίστα (List) 9 5 Συνδεδεµένη Λίστα (Linked List) 14 6 υαδικό έντρο Αναζήτησης (BSTree) 17 7 Ισοζυγισµένο έντρο (AVL Tree) 25 8 Σωρός (Heap) 34 9 Πίνακας Κατακερµατισµού (Hash Table) 41 10 Γράφηµα (Graph) 46 Α Ιεραρχία διασυνδέσεων δοµών δεδοµένων 53 Β Προτεινόµενες Ασκήσεις 56 Β.1 Στοίβα............................................ 56 Β.2 Ουρά............................................ 57 Β.3 Λίστα............................................ 58 i

Πληροφορίες Εργαστηρίου Σκοπός του εργαστηρίου Το εργαστήριο οµές εδοµένων αποσκοπεί στην εφαρµογή των τεχνολογιών που παρουσιάζονται στο ϑεωρητικό µέρος του µαθήµατος. Τα ϑέµατα, που καλύπτονται, σχετίζονται τόσο µε τις στατικές όσο και µε τις δυναµικές δοµές δεδοµένων. Το εργαστήριο δίνει ιδιαίτερη έµφαση στις τεχνικές της αφαίρεσης δεδοµένων και του προγραµµατισµού, που ϐασίζεται σε αντικείµενα. Την περίοδο αυτή ως γλώσσα κωδικοποίησης χρησιµοποιείται η Java. Με την ολοκλήρωση του µαθήµατος οι ϕοιτητές : Θα έχουν αποκτήσει καλή γνώση όλων των ϑεµελιωδών δοµών δεδοµένων και ϑα είναι σε ϑέση να τις χρησιµοποιούν για την υλοποίηση καλοσχεδιασµένων και αποδοτικών προγραµµάτων. Θα έχουν κατανοήσει τις έννοιες των αφηρηµένων τύπων δεδοµένων και των αντικειµένων καθώς και το ϱόλο που παίζουν στην ανάπτυξη των προγραµµατιστικών συστηµάτων. Επικοινωνία - Εργαστηριακοί συνεργάτες Στην περίπτωση που ϑέλετε να επικοινωνήσετε µε κάποιον από τους εργαστηριακούς συνεργάτες, µπορείτε να στείλετε e-mail µε τα στοιχεία σας (ονοµατεπώνυµο και τµήµα στο οποίο συµµετέχετε) στις παρακάτω διευθύνσεις : Ζερζελίδης Αλέξανδρος E-mail: alex@it.teithe.gr Κοντάκη Μαρία E-mail: kontaki@csd.auth.gr Κουµπή Βασιλάντα E-mail: vassik@otenet.gr Μιχαλόπουλος ηµήτρης E-mail: dmixalo@it.teithe.gr Μπαλτατζής ηµήτριος E-mail: jimbal@gen.auth.gr Χουβαρδάς Βασίλης E-mail: vchou@csd.auth.gr Επίσης από ϕέτος η διαχείρηση του Εργαστηρίου ϑα γίνεται µέσω του Blackboard R, οπότε για την επικοινωνία και τη διατύπωση ερωτήσεων, αποριών κλπ. µπορείτε να χρησιµοποιείτε την Περιοχή Συζητήσεων του Blackboard R 1

Εργαστήριο 1 Εισαγωγή στις οµές εδοµένων Ασκηση 1.1 Τα (ιδιωτικά- ηµιουργήστε την Student class, η οποία περιγράφει αφαιρετικά ένα ϕοιτητή. private) στοιχεία που ϑέλουµε να κρατήσουµε είναι : αριθµός µητρώου ϕοιτητή int όνοµα String επίθετο String ηλικία int ϕύλο char αριθµός απουσιών στο µάθηµα int ϐαθµός float Στη συνέχεια δηµιουργήστε την Lab class, η οποία περιγράφει αφαιρετικά ένα εργαστήριο κάποιου εργαστηριακού µαθήµατος. Η κλάση αυτή πρέπει Α. Να περιέχει τα παρακάτω private στοιχεία : Ονοµα τµήµατος String Μέγεθος τµήµατος int Β. να εκτελεί τα παρακάτω : Εισαγωγή ϕοιτητή στο µάθηµα Αναζήτηση ϕοιτητή µε ϐάση το αριθµό µητρώου ιαγραφή ϕοιτητή από το µάθηµα Εκτύπωση απουσιολογίου για το τµήµα ( αριθµός µητρώου, επώνυµο και όνοµα, αριθµός παρουσιών, ϐαθµός) Να υλοποιήσετε δύο δοµητές. Εναν µε όρισµα µόνο το όνοµα, όπου το τµήµα ϑα έχει προκα- ϑορισµένο µέγεθος και έναν µε ορίσµατα το όνοµα και το µέγεθος. ηµιουργήστε µια Testing class για να δοκιµάσετε τη λειτουργία όλων των παραπάνω. Σηµείωση : Η Lab class ϑα περιέχει ένα πίνακα, στον οποίο και ϑα αποθηκεύονται αντικείµενα της Student class. Σε κάθε κλάση, που δηµιουργείτε, καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. 2

ΕΡΓΑΣΤΗΡΙΟ 1. ΕΙΣΑΓΩΓΗ ΣΤΙΣ ΟΜΕΣ Ε ΟΜΕΝΩΝ Ασκηση 1.2 Υποθέστε τώρα ότι πρέπει να καταχωρήσουµε τους ϕοιτητές που παρακολουθούν το εργαστηριακό µάθηµα µε τίτλο οµές εδοµένων. ηµιουργήστε την LaboratoryCourse class, η οποία περιγράφει ένα εργαστηριακό µάθηµα. Η κλάση αυτή περιέχει όλα τα εργαστήρια που πραγµατοποιούνται πχ Τ1,Τ2,Τ3,.., τα οποία ϕυσικά είναι αντικείµενα της Lab class και περιέχουν τους ϕοιτητές, που είναι εγγεγραµµένοι στο αντίστοιχο εργαστήριο. Η LaboratoryCourse class πρέπει : Να εκτυπώνει το συνολικό πλήθος των ϕοιτητών, που παρακολουθούν το εργαστηριακό µάθηµα. Να εκτυπώνει το πλήθος τον ϕοιτητών, που πέτυχαν-απέτυχαν στο µάθηµα ανά εργαστήριο και συνολικά για το εργαστηριακό µάθηµα. Να εκτυπώνει το µέσο όρο του ϐαθµού των ϕοιτητών ανά εργαστήριο και το συνολικό µέσο όρο για το µάθηµα. Να εκτυπώνει τα ποσοστά επιτυχίας και αποτυχίας ανά εργαστήριο και συνολικά για το µάθηµα Ασκηση 1.3 ηµιουργήστε µια Management class, που ϑα εµφανίζει ένα κεντρικό µενού, από το οποίο ϑα µπορούµε να εκτελέσουµε όλες τις λειτουργίες της LaboratoryCourse class και επιπλέον : Να εισάγουµε κάποιο ϕοιτητή σε ένα τµήµα της αρεσκείας του (εφόσον υπάρχουν διαθέσιµες ϑέσεις). Να διαγράψουµε κάποιο ϕοιτητή δίνοντας ως παράµετρο µόνο τον αριθµό µητρώου. Να αναζητήσουµε ένα ϕοιτητή µε παράµετρο µόνο τον αριθµό µητρώου Επεκτείνετε το πληροφοριακό σας σύστηµα στη συνέχεια ώστε να µπορείτε να διαχειριστείτε 3 εργαστηριακά µαθήµατα του Τµήµατος Πληροφορικής του ΤΕΙ. 3

Εργαστήριο 2 Στοίβα (Stack) Εισαγωγή Στοίβα (stack) είναι µία δοµή δεδοµένων στην οποία νέα στοιχεία µπορούν να προστεθούν και να αφαιρεθούν µόνο από τη µία άκρη της (κορυφή της στοίβας). Συχνά µία στοίβα αναφέρεται και σαν µία δοµή τύπου LIFO (Last-In-First-Out), για να δηλώνεται έτσι ϱητά η ϐασική της ιδιότητα, ότι το στοιχείο που ϑα προστεθεί τελευταίο στη στοιβάδα ϑα αφαιρεθεί πρώτο ή ισοδύναµα το πρώτο στοιχείο που ϑα προστεθεί στη στοιβάδα, αναγκαστικά πρέπει να αφαιρεθεί τελευταίο. Βασικές πράξεις σε στοίβες Οι ϐασικές πράξεις (λειτουργίες), που ορίζονται για τον τύπο στοίβα αναφέρονται παρακάτω : Εισαγωγή στοιχείου σε στοίβα - push(p): οθείσης µίας στοίβας S, το στοιχείο P τοποθετείται στην κορυφή της στοίβας S, σαν τελευταίο στοιχείο. Εξαγωγή στοιχείου από στοίβα - pop(): Το στοιχείο P, που ϐρίσκεται στην κορυφή της στοίβας S, αφαιρείται από αυτήν και επιστρέφεται. Κορυφή της στοίβας - top(): Το στοιχείο P, που ϐρίσκεται στην κορυφή της στοίβας S, επιστρέ- ϕεται χωρίς όµως να αφαιρεθεί από αυτήν. Η παρακάτω διασύνδεση (interface) ορίζει τον αφηρηµένο τύπο δεδοµένων (abstract data type) στοίβα : Stack.java public interface Stack extends DataStructure { <p> Adds a new element i n t o the Stack @param Object public void push ( Object element ) throws StructureFullException ; <p> Removes the top element o f the Stack @return Object 4

ΕΡΓΑΣΤΗΡΙΟ 2. ΣΤΟΙΒΑ (STACK) public Object pop ( ) throws StructureEmptyException ; <p> Returns the top element o f the Stack @return Object public Object top ( ) throws StructureEmptyException ; } Ασκηση 2.1 Με ϐάση το Stack interface, που περιγράφει τη λειτουργία µιας στοίβας, να κατασκευάσετε την ArrayStack class µε χρήση πίνακα. Ο πίνακας ϑα είναι τύπου Object, ώστε να µπορούµε να αποθηκεύσουµε στη στοίβα αντικείµενα οποιουδήποτε τύπου. Η µέθοδος public boolean equals(datastructure otherstructure) (υπέρβαση της µεθόδου equals() της κλάσης Object) ϑα επιστρέφει true αν η τρέχουσα κλάση και η κλάση που δίνεται ώς όρισµα είναι ίδιες. Αν και ο ορισµός της ισότητας δύο οντοτήτων είναι υποκειµενικός και εξαρτάται από την εφαρµογή, στα πλαίσια της άσκησης ϑεωρείστε οτι δύο δοµές είναι ίσες όταν : οι δύο δοµές είναι του ιδίου τύπου (η otherstructure είναι ArrayStack) και οι δύο δοµές έχουν το ίδιο µέγεθος και τα αντικείµενα που είναι αποθηκευµένα και ϐρίσκονται στις αντίστοιχες ϑέσεις στις δύο στοίβες είναι ίσα. Ο έλεγχος του τύπου µπορεί να γίνει µε τη χρήση του τελεστή instanceof, την otherstructure.getclass() ή του class literal ArrayStack.class Παραλλαγή της άσκησης : (α) Για τη σάρωση των στοιβών µπορείτε να χρησιµοποιήσετε µία ή δύο ϐοηθητικές στοίβες. (ϐ) Προσθέστε στα κριτήρια ισότητας της equals() και την ισότητα του µεγέθους των αποθηκευτικών χώρων. Σε κάθε κλάση, που δηµιουργείτε, καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Ασκηση 2.2 ηµιουργήστε µια στοίβα µε τυχαίους αριθµούς. Εκτελέστε τις ϐασικές λειτουργίες της στοίβας σε µια νέα κλάση TestStack class. Ασκηση 2.3 Να γραφεί πρόγραµµα Java για τον έλεγχο της σωστής χρήσης των παρενθέσεων σε µία αριθµητική παράσταση χρησιµοποιώντας τη δοµή δεδοµένων στοίβα. Το πρόγραµµα να επιστρέφει 5

ΕΡΓΑΣΤΗΡΙΟ 2. ΣΤΟΙΒΑ (STACK) µήνυµα εάν η αριθµητική παράσταση είναι σωστή ή εάν έχει λάθος, να αναφέρει το σηµείο που ϐρήκε το λάθος. Επεκτείνετε το πρόγραµµα σας ώστε να γίνεται έλεγχος σε {} και σε []. Παραδείγµατα : (1 + 3) 3 (2 + 2) Result: No error 1 + 3) 3 (2 + 2) Result: error at position 4!! Ασκηση 2.4 Σε µια νέα κλάση να κατασκευάσετε µια στοίβα, που να περιέχει τα γράµµατα του λατινικού αλφαβήτου, και στη συνέχει να γράψετε τον κώδικα της µεθόδου : public static Stack reversestack(stack source) η οποία δέχεται µια στοίβα source στην είσοδο και επιστρέφει µια νέα στοίβα µε τα στοιχεία της source σε αντίστροφη σειρά. Παράδειγµα εκτέλεσης Αρχική stack = [a b c d e f g h i j k l m n o p q r s t u v w x y z ] Ανεστραµµένη stack = [z y x w v u t s r q p o n m l k j i h g f e d c b a ] 6

Εργαστήριο 3 Ουρά (Queue) Εισαγωγή Ουρά (queue) είναι µια δοµή δεδοµένων, στην οποία µπορούν να προστεθούν στοιχεία µόνο στη µία άκρη (πίσω) και να αφαιρεθούν µόνο από την άλλη (µπροστά). Η ουρά αναφέρεται και σαν δοµή τύπου FIFO (First-In- First-Out). Βασικές πράξεις σε ουρές Οι ϐασικές πράξεις (λειτουργίες), που ορίζονται για τον τύπο ουρά είναι οι εξής : Εισαγωγή στοιχείου σε ουρά - enqueue(p): οθείσης µίας ουράς Q, το στοιχείο P τοποθετείται στο πίσω µέρος της σαν τελευταίο στοιχείο. Εξαγωγή στοιχείου από ουρά - dequeue(p): οθείσης µίας ουράς Q, το στοιχείο P, που ϐρίσκεται στην αρχή της, αφαιρείται από αυτήν και επιστρέφεται. Πρώτο στοιχείο της ουράς - front(): οθείσης µίας ουράς Q, επιστρέφει το πρώτο στοιχείο της ουράς, χωρίς όµως να το αφαιρεί από αυτήν. Το παρακάτω interface ορίζει τον αφηρηµένο τύπο δεδοµένων ουρά. Queue.java public interface Queue extends DataStructure { <p> Adds a new element i n t o the Queue @param Object public void enqueue ( Object element ) throws StructureFullException ; <p> Removes the f r o n t element o f the Stack @return Object 7

ΕΡΓΑΣΤΗΡΙΟ 3. ΟΥΡΑ (QUEUE) public Object dequeue ( ) throws StructureEmptyException ; <p> Returns the f r o n t element o f the Queue @return Object public Object f r o n t ( ) throws StructureEmptyException ; } Ασκηση 3.1 Με ϐάση το Queue interface, που περιγράφει τη λειτουργία µιας ουράς, να κατασκευάσετε την ArrayQueue class µε χρήση πίνακα. Για το σκεπτικό υλοποίησης της µεθόδου public boolean equals(datastructure otherstructure) ισχύει ότι έχει αναφερθεί στη άσκηση 2.1 Σε κάθε κλάση, που δηµιουργείτε, καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Ασκηση 3.2 ηµιουργήστε µια ουρά µε τυχαίους αριθµούς. Εκτελέστε τις ϐασικές λειτουργίες της ουράς σε µια νέα κλάση TestQueue class. Ασκηση 3.3 Να προσθέσετε κατάλληλο κώδικα στην ArrayQueue class, που να αντιµετωπίζει το ϕαινόµενο της εικονικής υπερχείλισης, σύµφωνα µε το οποίο η δοµή παρουσιάζεται γεµάτη ή αδυνατεί να αποθηκεύσει στοιχεία, ενώ στην πραγµατικότητα υπάρχουν κενές ϑέσεις στην εσωτερική µονάδα αποθήκευσης. Στην περίπτωση της ArrayQueue η µονάδα αποθήκευσης είναι ένας πίνακας αντικειµένων. Ασκηση 3.4 Να κατασκευάσετε την ArrayCircleQueue class ως κυκλική ουρά. Πληροφορίες σχετικά µε την κατασκευή της κυκλικής ουράς ϑα ϐρείτε στις σηµειώσεις της ϑεωρίας. 8

Εργαστήριο 4 Λίστα (List) Εισαγωγή Η λίστα (list) είναι µια δοµή δεδοµένων, στην οποία µπορούν να προστεθούν και να αφαιρεθούν στοιχεία και από τις δύο µεριές. Η λίστα είναι η πιο ευέλικτη δοµή δεδοµένων, καθώς η λειτουργία της ουσιαστικά περικλείει την λειτουργία τόσο της στοίβας όσο και της ουράς. Ετσι, µπορούµε να χρησιµοποιήσουµε µια λίστα ως µια στοίβα ή ως µια ουρά σε οποιαδήποτε εφαρµογή. Η ευελιξία της λίστας την καθιστά απαραίτητο προγραµµατιστικό εργαλείο και συνήθως ενσωµατώνεται στις σύγχρονες γλώσσες προγραµµατισµού. Βασικές πράξεις σε λίστες Οι ϐασικές πράξεις (λειτουργίες) που ορίζονται για τον τύπο λίστα είναι οι εξής : Πρώτο στοιχείο της λίστας - getfirst(): οθείσης µίας λίστας L, επιστρέφει το πρώτο στοιχείο της λίστας, χωρίς όµως να το αφαιρεί από αυτήν. Τελευταίο στοιχείο της λίστας - getlast(): οθείσης µίας λίστας L, επιστρέφει το τελευταίο στοιχείο της λίστας, χωρίς όµως να το αφαιρεί από αυτήν. Εισαγωγή στοιχείου σε λίστα - insertfirst(p): οθείσης µίας λίστας L, το στοιχείο P τοποθετείται στο µπροστά µέρος της ως πρώτο στοιχείο. Εισαγωγή στοιχείου σε λίστα - insertlast(p): οθείσης µίας λίστας L, το στοιχείο P τοποθετείται στο πίσω µέρος της σαν τελευταίο στοιχείο. Εξαγωγή στοιχείου από λίστα - removefirst(): οθείσης µίας λίστας L, το στοιχείο P, που ϐρίσκεται στην αρχή της, αφαιρείται από αυτήν και επιστρέφεται. Εξαγωγή στοιχείου από λίστα - removelast(): οθείσης µίας λίστας L, το στοιχείο P, που ϐρίσκεται στο τέλος της, αφαιρείται από αυτήν και επιστρέφεται. Το παρακάτω interface ορίζει τον αφηρηµένο τύπο δεδοµένων λίστα. List.java public interface L i s t extends DataStructure { 9

ΕΡΓΑΣΤΗΡΙΟ 4. ΛΙΣΤΑ (LIST) <p> Returns the f i r s t object of the linked l i s t @return Object public Object g e t F i r s t ( ) throws StructureEmptyException ; <p> Returns the last object of the linked l i s t @return Object public Object getlast ( ) throws StructureEmptyException ; <p> Inserts an object at the beggining of the l i s t @param Object public void insertfirst ( Object data ) throws StructureFullException ; <p> Inserts an object at the end of the l i s t @param Object public void insertlast ( Object data ) throws StructureFullException ; <p> Removes the f i r s t object from the l i s t @return Object public Object removefirst ( ) throws StructureEmptyException ; <p> Removes the last object from the l i s t @return Object public Object removelast ( ) throws StructureEmptyException ; } Ασκηση 4.1 Με ϐάση το List interface, που περιγράφει τη λειτουργία µιας λίστας, να κατασκευάσετε την ArrayList ςλασς µε χρήση πίνακα. 10

ΕΡΓΑΣΤΗΡΙΟ 4. ΛΙΣΤΑ (LIST) Για το σκεπτικό υλοποίησης της µεθόδου public boolean equals(datastructure otherstructure) ισχύει ότι έχει αναφερθεί στη άσκηση 2.1. Σε κάθε κλάση, που δηµιουργείτε, καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Ασκηση 4.2 Στον κώδικα της ArrayList να προσθέσετε τις µεθόδους private void alignmentfirst(), private void alignmentlast() που αντιµετωπίζουν το ϕαινόµενο της εικονικής υπερχείλισης, σύµφωνα µε το οποίο η δοµή παρουσιάζεται γεµάτη ή αδυνατεί να αποθηκεύσει στοιχεία, ενώ στην πραγµατικότητα υπάρχουν κενές ϑέσεις στην εσωτερική µονάδα αποθήκευσης - στην περίπτωση της ArrayList η µονάδα απο- ϑήκευσης είναι ένας πίνακας αντικειµένων. Οταν γράψετε τον κώδικα των µεθόδων, να τις καλέσετε αντίστοιχα στον κώδικα των µεθόδων που εισάγουν στοιχεία στα δυο άκρα της λίστας ( insertfirst(..), insertlast(..) ) Για να δοκιµάσετε την ορθότητα της λύσης που κατασκευάσατε γράψτε και εκτελέστε τον παρακάτω κώδικα : import java. u t i l.random; TestList.java public class TestList { public static void main ( String [ ] args ) { Random r = new Random ( ) ; ArrayList mylist = new ArrayList ( 1 0 ) ; } } for ( int i =0; i <10; i ++){ mylist. insertlast ( r. nextint (1000)); System. out. println ( mylist ) ; } Αναλυτικά : Εισαγωγή στοιχείων στο τέλος της λίστας Σύµφωνα µε την υλοποίηση της λίστας µε πίνακα που έχουµε παρουσιάσει στο εργαστηριακό µάθηµα το αποτέλεσµα της παραπάνω εκτέλεσης είναι το ακόλουθο : Με εικονική υπερχείλιση : Start [ 292 ] End Start [ 292 886 ] End Start [ 292 886 342 ] End Start [ 292 886 342 783 ] End Start [ 292 886 342 783 519 ] End 11

ΕΡΓΑΣΤΗΡΙΟ 4. ΛΙΣΤΑ (LIST) Exception in thread "main" java.lang.arrayindexoutofboundsexception:10 at Lab5.Code.AList.ArrayList.insertLast(ArrayList.java:113) at TestList.main(TestList.java:20) Ο λόγος που δηµιουργείται µια ArrayIndexOutOfBoundsException είναι ότι η µεταβλητή last, που είναι υπεύθυνη για την εισαγωγή στοιχείων στο τέλος της λίστας, έχει υπερβεί τα επιτρεπτά όρια και έχει πάρει την τιµή 10. Σκοπός είναι όταν ολοκληρώσετε την µέθοδο alignmentlast() να πάρετε το ακόλουθο αποτέλεσµα : Χωρίς εικονική υπερχείλιση : Start [ 216 ] End Start [ 216 814 ] End Start [ 216 814 970 ] End Start [ 216 814 970 674 ] End Start [ 216 814 970 674 446 ] End Start [ 216 814 970 674 446 228 ] End Start [ 216 814 970 674 446 228 209 ] End Start [ 216 814 970 674 446 228 209 515 ] End Start [ 216 814 970 674 446 228 209 515 750 ] End Start [ 216 814 970 674 446 228 209 515 750 402 ] End Εισαγωγή στοιχείων στην αρχή της λίστας Για την περίπτωση ελέγχου της εισαγωγής στοιχείων από την αρχή της λίστας αντικαταστήστε στον παραπάνω κώδικα (TestList.java) την µέθοδο εισαγωγής mylist.insertlast(...) µε την mylist.insertfirst(...). Ενα τέτοιο παράδειγµα εκτέλεσης είναι το παρακάτω : Με εικονική υπερχείλιση : Start [ 526 ] End Start [ 277 526 ] End Start [ 738 277 526 ] End Start [ 928 738 277 526 ] End Start [ 838 928 738 277 526 ] End Exception in thread main java.lang.arrayindexoutofboundsexception: -1 at Lab5.Code.AList.ArrayList.insertFirst(ArrayList.java:95) at TestList.main(TestList.java:20) Ο λόγος, που δηµιουργείται µια ArrayIndexOutOfBoundsException είναι ότι η µεταβλητή first, που είναι υπεύθυνη για την εισαγωγή στοιχείων στην αρχή της λίστας έχει υπερβεί τα επιτρεπτά όρια και έχει πάρει την τιµή 1. Σκοπός είναι όταν ολοκληρώσετε την µέθοδο alignmentfirst() να πάρετε το ακόλουθο αποτέλεσµα : Χωρίς εικονική υπερχείλιση : Start [ 215 ] End Start [ 692 215 ] End Start [ 981 692 215 ] End Start [ 999 981 692 215 ] End Start [ 635 999 981 692 215 ] End Start [ 811 635 999 981 692 215 ] End Start [ 932 811 635 999 981 692 215 ] End 12

ΕΡΓΑΣΤΗΡΙΟ 4. ΛΙΣΤΑ (LIST) Start [ 545 932 811 635 999 981 692 215 ] End Start [ 119 545 932 811 635 999 981 692 215 ] End Start [ 383 119 545 932 811 635 999 981 692 215 ] End 13

Εργαστήριο 5 Συνδεδεµένη Λίστα (Linked List) Ασκηση 5.1 firstnode lastnode Item nextnode ÐëåõñÜ First ListNode ÐëåõñÜ Last Σχήµα 5.1: Απλά συνδεδεµένη λίστα Για να κατασκευάσετε µια απλά συνδεδεµένη λίστα, σύµφωνα µε το σχήµα 5.1, πρέπει να δηµιουργήσετε τις παρακάτω κλάσεις : ListNode class η οποία περιγράφει ένα κόµβο µιας απλά συνδεδεµένης λίστας (το πλαίσιο δίνεται παρακάτω) SimpleLinkedList class που υλοποιεί το List interface ListNode.java public class ListNode { // Instance methods public Object getnodeitem ( ) {... } public ListNode getnextnode ( ) {... } public void setnodeitem ( Object i t ) {... } public void setnextnode ( ListNode n) {... } public boolean hasnext ( ) {... } public String tostring ( ) {... } } // Instance f i e l d s private Object item ; private ListNode nextnode ; 14

ΕΡΓΑΣΤΗΡΙΟ 5. ΣΥΝ Ε ΕΜΕΝΗ ΛΙΣΤΑ (LINKED LIST) Για το σκεπτικό υλοποίησης της µεθόδου public boolean equals(datastructure otherstructure) ισχύει ότι έχει αναφερθεί στη άσκηση 2.1 Σε κάθε κλάση, που δηµιουργείτε καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Ασκηση 5.2 Να γραφεί πρόγραµµα (SLListManagement.java) για την διαχείριση µίας λίστας, η οποία περιέχει Strings. Η διαχείριση να γίνεται µέσω ενός µενού όπως το παρακάτω : ********** LINKED LIST MANAGEMENT ********** 1- INSERT ELEMENT AT THE BEGINNING OF THE LIST 2- INSERT ELEMENT AT THE END OF THE LIST 3- DELETE FROM THE BEGINNING OF THE LIST 4- DELETE FROM THE END OF THE LIST 5- LIST LENGTH 6- IS THE LIST EMPTY 7- PRINT LIST 8- EXIT INPUT YOUR CHOICE (E.G. 5): Ασκηση 5.3 Για να κατασκευάσετε µια διπλά συνδεδεµένη λίστα πρέπει να δηµιουργήσετε τις παρακάτω κλάσεις : DLListNode class η οποία περιγράφει ένα κόµβο µιας διπλά συνδεδεµένης λίστας και επεκτείνει τη ListNode class (το πλαίσιο δίνεται παρακάτω) DoubleLinkedList class που υλοποιεί το List interface DLListNode.java public class DLListNode extends ListNode { // Instance methods public DLListNode getpreviousnode ( ) {... } public void setpreviousnode ( DLListNode n) {... } public boolean hasprevious ( ) {... } } // Instance f i e l d s private DLListNode previousnode ; Ασκηση 5.4 Να δώσετε µια υλοποίηση του Stack interface µε χρήση SimpleLinkedList Ασκηση 5.5 Να δώσετε µια υλοποίηση του Queue interface µε χρήση DoubleLinkedList 15

ΕΡΓΑΣΤΗΡΙΟ 5. ΣΥΝ Ε ΕΜΕΝΗ ΛΙΣΤΑ (LINKED LIST) Ασκηση 5.6 Να γράψετε τον κώδικα της µεθόδου : public static DoubleLinkedList reverselist(doublelinkedlist source) η οποία δέχεται µια λίστα source στην είσοδο και επιστρέφει µια νέα λίστα µε τα στοιχεία της source σε αντίστροφη σειρά. Η αρχική λίστα παραµένει αµετάβλητη. Παράδειγµα Αρχική λίστα : [α ϐ ς δ ε ϕ γ η ι ϑ κ λ µ ν ο π χ ϱ ς τ υ ω ξ ψ Ϲ ] Ανεστραµµένη λίστα : [Ϲ ψ ξ ω υ τ ς ϱ χ π ο ν µ λ κ ϑ ι η γ ϕ ε δ ς ϐ α ] 16

Εργαστήριο 6 υαδικό έντρο Αναζήτησης (BSTree) Εισαγωγή Οι περισσότερες δοµές δεδοµένων, που εξετάσαµε µέχρι τώρα (λίστες, στοίβες, ουρές) ήταν γραµ- µικές (ή δοµές δεδοµένων µιας διάστασης). Στην παράγραφο αυτή ϑα ασχοληθούµε µε τις µηγραµµικές δοµές δεδοµένων, που ονοµάζονται δέντρα. Συναντάµε τα δέντρα σε πάρα πολλές εφαρ- µογές της επιστήµης των υπολογιστών. Κλασσικό παράδειγµα αποτελεί η οργάνωση του συστήµατος των αρχείων, µε τη µορφή δέντρου, που ακολουθούν τα περισσότερα λειτουργικά συστήµατα (π.χ. DOS, UNIX, σχήµα 6.1). Τα δέντρα χρησιµοποιούνται επίσης πολύ συχνά, σαν µοντέλα αναπαράστασης προβληµάτων της καθηµερινής µας Ϲωής. Χαρακτηριστικά παραδείγµατα αποτελούν το γενεαλογικό δέντρο και η δενδροειδής αναπαράσταση αγώνων κυπέλου, µεταξύ οµάδων (σχήµα 6.2). \ {root directory} Accessories Program Files MSoffice2000 Autoexec.bat Chkdsk.exe NetMeeting Windows Command.com JavaFiles Readme.txt Programs Program1.java Program2.java Σχήµα 6.1: ενδροειδής αναπαράσταση αρχείων στα Windows Παρά το γεγονός ότι η έννοια του δέντρου γίνεται έµµεσα κατανοητή από έναν οποιονδήποτε οπτικό τρόπο αναπαράστασης του, η δοµή δεδοµένων δέντρο πρέπει να οριστεί µε έναν αυστηρά µαθηµατικό τρόπο. Υπάρχουν πολλοί ισοδύναµοι ορισµοί ενός δέντρου, δίνουµε στη συνέχεια δύο από αυτούς. Ορισµός 6.1. έντρο (tree) είναι ένα σύνολο Τ από κόµβους (nodes), τέτοιο ώστε είτε : Το Τ είναι κενό ή Το Τ περιλαµβάνει ένα ξεχωριστό κόµβο, R, που ονοµάζεται ϱίζα (root ) του Τ και οι υπόλοιποι κόµβοι Τ - {R} χωρίζονται σε µηδέν ή περισσότερα σύνολα κόµβων, T 1, T 2,..., T n, που είναι 17

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) ÁÑÇÓ ÏËÕÌÐÉÁÊÏÓ ÐÁÏÊ ÏÖÇ ÁÑÇÓ ÏÖÇ ÁÑÇÓ ÇÑÁÊËÇÓ ÐÁÏ ÄÏÎÁ AEK ÐÁÏ AEK AEK ÊÕÐÅËÏÕ ÏÓ Σχήµα 6.2: Αναπαράσταση αγώνων κυπέλου µε µορφή δέντρου ξένα µεταξύ τους και τα οποία είναι µε τη σειρά τους δέντρα. Τα T 1, T 2,..., T n, ονοµάζονται υπο-δέντρα του Τ. Ορισµός 6.2. έντρο είναι µία συλλογή από στοιχεία, που ονοµάζονται κόµβοι. Οι κόµβοι του δέντρου συνδέονται µεταξύ τους µε τη ϐοήθεια ακµών (arcs) µε ϐάση τους εξής κανόνες : Υπάρχει ένας και µόνον ένας κόµβος (ϱίζα - root) στον οποίο δεν καταλήγει καµία ακµή Σε όλους τους υπόλοιπους κόµβους καταλήγει υποχρεωτικά µία και µόνο µία ακµή. Ο πρώτος ορισµός είναι αναδροµικός και είναι καλό να τον έχει κανείς υπ όψη του, όταν ϑα συζητήσουµε αργότερα, τις διάφορες ιδιότητες και τους αλγόριθµους επεξεργασίας των δέντρων. Ο δεύτερος ορισµός ϐρίσκεται σε µεγαλύτερη αντιστοιχία µε τον οπτικό τρόπο αναπαράστασης ενός δέντρου. Ας εξετάσουµε τους δύο ορισµούς χρησιµοποιώντας για παράδειγµα το δέντρο του σχήµατος 6.3. Σύµφωνα µε τον δεύτερο ορισµό ϐλέπουµε ότι µόνο στον κόµβο A, που αποτελεί τη ϱίζα του δέντρου δεν καταλήγει καµία ακµή, ενώ σε όλους τους άλλους κόµβους καταλήγει µία µόνο ακµή. Παρατηρήστε ότι συνήθως σχεδιάζουµε ένα δέντρο µε τη ϱίζα του επάνω και τους υπόλοιπους κόµβους από κάτω (παρόλο που αυτό µοιάζει παράξενο!). A B C D E F G H I J K L M Σχήµα 6.3: Συνήθης οπτική αναπαράσταση δέντρου 18

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) Σύµφωνα µε τον πρώτο ορισµό το δέντρο αποτελείται από ένα σύνολο 13 κόµβων : T = {A, B, C, D, E, F, G, H, I, J, K, L, M} Η ϱίζα του είναι A και το σύνολο T {A} χωρίζεται σε τρία υπο-δέντρα : T 1 = {B, E, F, G}, T 2 = {C, H, I} και T 3 = {D, J, K, L, M} Τα T 1, T 2 και T 3 έχουν σαν ϱίζα τον κόµβο B, C, και D αντίστοιχα. Τα σύνολα T 1 {B}, T 2 {C} και T 3 {D} χωρίζονται αντίστοιχα στα υπο-δέντρα : T 1.1 = {E}, T 1.2 = {F }, T 1.3 = {G} T 2.1 = {H}, T 2.2 = {I} T 3.1 = {J}, T 3.2 = {K}, T 3.3 = {L}, T 3.4 = {M} Κάθε ένα από τα τελευταία υπο-δέντρα είναι σύνολα που περιέχουν έναν µόνον κόµβο. Ο κόµβος αυτός αποτελεί και τη ϱίζα του αντίστοιχου υπο-δέντρου και αν αφαιρεθεί το σύνολο που προκύπτει είναι το κενό (περίπτωση (α) του ορισµού). Μερικοί ακόµα ορισµοί-ιδιότητες των δέντρων είναι απαραίτητοι : Κάθε κόµβος (εκτός από τη ϱίζα) έχει ακριβώς έναν κόµβο από πάνω του, ο οποίος ονοµάζεται πατέρας (father). Για παράδειγµα στο δέντρο του παραπάνω σχήµατος, ο A είναι ο πατέρας του B και ο D είναι ο πατέρας του K. Οι κόµβοι που ϐρίσκονται ακριβώς κάτω από έναν κόµβο (συνδέονται µε ακµές που ξεκινούν από αυτόν), ονοµάζονται παιδιά του (children). Πολλές ϕορές µπορεί να αναφερόµαστε (κατ αναλογία µε τα γενεαλογικά δέντρα) στους απογόνους (successors) ή προγόνους (ancestors) ενός κόµβου. Στο παράδειγµα του παραπάνω σχήµατος, οι κόµβοι E, F, G είναι παιδιά του κόµβου B. Οι κόµβοι H και I είναι απόγονοι του A (όχι οι µοναδικοί) και ο κόµβος G έχει πρόγονο τον Α. Ενας κόµβος ο οποίος δεν έχει κανένα µη-κενό υπό-δέντρο (κόµβος χωρίς παιδιά) ονοµάζεται τερµατικός (terminal) κόµβος ή ϕύλλο (leaf ) του δέντρου. Στο δέντρο του σχήµατος 6.3, τερ- µατικοί κόµβοι είναι οι E, F, G, H, I, J, K, L και M. Ολοι οι υπόλοιποι κόµβοι του δέντρου ονοµάζονται µη-τερµατικοί (non-terminals). Πολλές ϕορές οι τερµατικοί κόµβοι ενός δέντρου αναφέρονται και σαν εξωτερικοί (externals) ενώ οι µη τερµατικοί σαν εσωτερικοί (internals). Μια ακολουθία κόµβων, ενός δέντρου, οι οποίοι συνδέονται διαδοχικά µεταξύ τους µε τη ϐοή- ϑεια ακµών, ονοµάζεται µονοπάτι (path). Οι ακολουθίες < A, B, G > και < A, D, K > είναι δύο από τα µονοπάτια του δέντρου του σχήµατος 6.3. Οι κόµβοι ενός δέντρου χωρίζονται σε επίπεδα ( levels). Ενας κόµβος ϐρίσκεται στο επίπεδο N, εάν N είναι ο αριθµός των ακµών του µονοπατιού, που συνδέει τη ϱίζα µε τον κόµβο αυτό. Στο παράδειγµα του σχήµατος 6.3, ο A ανήκει στο επίπεδο 0, οι B, C, D ανήκουν στο επίπεδο 1, ενώ οι κόµβοι E, F, G, H, I, J, K, L, M αποτελούν το επίπεδο 2. Ορίζουµε σαν ύψος (heigth) ή ϐάθος (depth) ενός κόµβου τον αριθµό του επιπέδου στο οποίο ϐρίσκεται ο κόµβος αυτός. Το ύψος (ή ϐάθος) ενός δέντρου ταυτίζεται µε το µέγιστο ύψος (ή ϐάθος) των κόµβων του δέντρου. Ονοµάζουµε ϐαθµό ( degree) ενός κόµβου τον αριθµό των υπό-δέντρων του Ονοµάζουµε δάσος (forest) ένα σύνολο από N (N 0) δέντρα, που είναι ξένα µεταξύ τους. 19

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) D B F A C G E Σχήµα 6.4: υαδικό δέντρο υαδικά δέντρα Ορισµός 6.3. υαδικό δέντρο ( binary tree) είναι ένα δέντρο του οποίου κάθε κόµβος έχει το πολύ δύο υπο-δέντρα. Τα υπο-δέντρα του δυαδικού δέντρου ονοµάζονται αριστερό και δεξιό υπο-δέντρο αντίστοιχα (σχήµα 6.4). Κατασκευή υαδικού έντρου µε τη ϐοήθεια δεικτών Μπορούµε να αναπαραστήσουµε ένα δυαδικό δέντρο µε τη ϐοήθεια µιας δυναµικής δοµής δεδοµένων. Κάθε κόµβος του δέντρου αναπαρίσταται µε τη ϐοήθεια µιας τριάδας η οποία περιλαµβάνει ένα πεδίο για την αποθήκευση της πληροφορίας του κόµβου και δύο πεδία τύπου δείκτη σε κόµβο δέντρου, που δείχνουν στο αριστερό και δεξιό υπο-δέντρο αντίστοιχα. Η τριάδα αυτή των δηλώσεων ορίζονται σαν µέλη της κλάσης TreeNode. ίνεται επίσης στα πλαίσια της κλάσης η πράξη δηµιουργίας ενός τερµατικού κόµβου : TreeNode.java public class TreeNode { public TreeNode ( Comparable obj ) {... } public Comparable getnodedata ( ) {... } public TreeNode getleftnode ( ) {... } public TreeNode getrightnode ( ) {... } public boolean isleaf ( ) {... } public void setleftnode ( TreeNode node ) {... } public void setrightnode ( TreeNode node ) {... } } // Instance f i e l d s private TreeNode l e f t ; private TreeNode right ; private Comparable item ; 20

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) Root Root.left D Root.right B F A C null G null null E null null null null null Σχήµα 6.5: υαδικό δέντρο Για να αναφερθούµε σε ένα δυαδικό δέντρο χρησιµοποιούµε ένα δείκτη ( pointer), ο οποίος δείχνει στην ϱίζα του. Ο δείκτης αυτός (root) ορίζεται στα πλαίσια της κλάσης που υλοποιεί το δέντρο αυτό Η χρήση δυναµικής δοµής δεδοµένων είναι τις περισσότερες ϕορές η πιο κατάλληλη για την αναπαράσταση ενός δυαδικού δέντρου και είναι αυτή που ϑα χρησιµοποιήσουµε για την υλοποίηση των διαφόρων πράξεων-λειτουργιών που ορίζονται πάνω στα δυαδικά δέντρα. Στο σχήµα 6.5 ϕαίνεται η γραφική αναπαράσταση του δέντρου του σχήµατος 6.4 µε τη ϐοήθεια δεικτών. Παρατηρήστε ότι στην περίπτωση που το αριστερό ή το δεξιό υπο-δέντρο ενός κόµβου είναι κενό τότε η αντίστοιχη τιµή του δείκτη left ή right έχει την τιµή null. Κατασκευή υαδικού έντρου Αναζήτησης Το δυαδικό δέντρο αναζήτησης αποτελεί µία ειδική κατηγορία δυαδικού δέντρου, το οποίο κατασκευάζεται µε ϐάση τον παρακάτω αλγόριθµο. Το πρώτο δεδοµένο χρησιµοποιείται για τη δηµιουργία της ϱίζας του δέντρου. Τα επόµενα δεδοµένα τοποθετούνται στο δέντρο, έτσι ώστε σε σχέση µε οποιονδήποτε κόµβο να ισχύει το εξής : όλες οι τιµές που ϐρίσκονται στο αριστερό υπο-δέντρο είναι µικρότερες από την τιµή του κόµβου και όλες οι τιµές που ϐρίσκονται στο δεξιό υπο-δέντρο είναι µεγαλύτερες. Ασκηση 6.1 Για να κατασκευάσετε ένα δυαδικό δέντρο αναζήτησης πρέπει να δηµιουργήσετε τις παρακάτω κλάσεις : TreeNode class η οποία περιγράφει ένα κόµβο ενός δυναµικού BSTree. DBSTree class που υλοποιεί το BSTree interface 21

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) BSTree.java public interface BSTree extends DataStructure { <p> Adds a new item into the tree </p> public void add ( Comparable item ) throws StructureFullException ; } <p> Removes an item from the t r e e public void remove ( Comparable item ) throws StructureEmptyException ; Για το σκεπτικό υλοποίησης της µεθόδου public boolean equals(datastructure otherstructure) ισχύει ότι έχει αναφερθεί στη άσκηση 2.1 Σε κάθε κλάση, που δηµιουργείτε καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Τα δεδοµένα των κόµβων ϑα πρέπει να υλοποιούν το interface Comparable. Στο site της SUN αναφέρεται σχετικά : public interface Comparable This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class s natural ordering, and the class s compareto method is referred to as its natural comparison method.... compareto public int compareto(object o) Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. Περισσότερα για το interface Comparable: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/comparable.html Ασκηση 6.2 Στη συνέχεια να προσθέσετε στην DBSTree class τον παρακάτω κώδικα που εκτελεί τις 3 διελεύσεις δέντρου (inorder, preorder, postorder) που αναφέρονται στην ϑεωρία. DBSTree.java 22

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) //....... public void inordertraversal ( ) { System. out. println ( "INORDER TRAVERSAL" ) ; inorder ( 0 ) ; System. out. println ( ) ; } public void preordertraversal ( ) { } System. out. println ( "PREORDER TRAVERSAL" ) ; preorder ( 0 ) ; System. out. println ( ) ; public void postordertraversal ( ) { } System. out. println ( "POSTORDER TRAVERSAL" ) ; postorder ( 0 ) ; System. out. println ( ) ; // Recursive inorder private void inorder ( int position ) {... } // Recursive preorder private void preorder ( int position ) {... } // Recursive postorder private void postorder ( int position ) {... } Ασκηση 6.3 Να γράψετε την κλάση BSTreeManagement class που ελέγχει την ορθότητα των διελεύσεων που κατασκευάσατε στην προηγούµενη άσκηση : BSTreeManagement.java public class BSTreeManagement { public static void main ( String args [ ] ) { DBSTree tree = new DBSTree ( ) ; int matrix [ ] = {42,12,19,50,17,10,69,54,43,8}; for ( int i =0; i <matrix. length ; i ++) 23

ΕΡΓΑΣΤΗΡΙΟ 6. ΥΑ ΙΚΟ ΕΝΤΡΟ ΑΝΑΖΗΤΗΣΗΣ (BSTREE) tree. add ( matrix [ i ] ) ; System. out. println ( tree ) ; } } tree. inordertraversal ( ) ; tree. preordertraversal ( ) ; tree. postordertraversal ( ) ; Ασκηση 6.4 Με ϐάση το BSTree interface, που περιγράφει τη λειτουργία ενός δυαδικού δέντρου αναζήτησης, να κατασκευάσετε την ArrayBSTree class µε χρήση πίνακα. Στη συνέχεια να προσθέσετε στην ArrayBSTree class τις 3 διελεύσεις δέντρου (inorder, preorder, postorder) που αναφέρονται στην ϑεωρία. Τέλος να εκτελέσετε την BSTreeManagement class που ελέγχει την ορθότητα των διελεύσεων που κατασκευάσατε. 24

Εργαστήριο 7 Ισοζυγισµένο έντρο (AVL Tree) Εισαγωγή Εκτός από τα δυαδικά δέντρα αναζήτησης (binary search trees) που εξετάσαµε σε προηγούµενο εργαστήριο, υπάρχουν αρκετά είδη δέντρων αναζήτησης µε ξεχωριστό ενδιαφέρον. Τα ισοζυγισµένα δέντρα αναζήτησης (self-balancing binary search tree) είναι δυαδικά δέντρα αναζήτησης, τα οποία κρατούν το ύψος τους όσο το δυνατό µικρότερο, µέσω αυτόµατων διαδικασιών αναπροσαρµογής των κόµβων τους. Οι περισσότερες λειτουργίες, που εκτελούνται σε ένα δυαδικό δέντρο αναζήτησης, α- παιτούν υπολογιστικό χρόνο ευθέως ανάλογο µε το ύψος του δέντρου. Είναι λοιπόν προφανές ότι µας ενδιαφέρει να παραµένει το ύψος του δέντρου όσο το δυνατό µικρότερο. Τα απλά BS Trees έχουν το µειονέκτηµα της γρήγορης ανάπτυξης ύψους σε σχετικά συνηθησµένες καταστάσεις. Για παράδειγµα, αν εισάγουµε ταξινοµηµένους αριθµούς σε ένα απλό BS Tree ϑα καταλήξουµε σε ένα δέντρο-κλαδί µε ύψος όσο και το πλήθος των εισαγόµενων κόµβων (σχήµα 7.1). Σε αυτή την περίπτωση η αναζήτηση, για παράδειγµα (στην οποία και πλεονεκτεί το BS Tree), ϑα χρειαστεί τον ίδιο χρόνο, που απαιτείται και σε µια λίστα. Αν γνωρίζαµε τα εισαγόµενα στοιχεία εκ τον προτέρων ϑα µπορούσαµε να κρατήσουµε την ανάπτυξη του δέντρου σε χαµηλό ύψος εισάγοντας τα στοιχεία σε τυχαία σειρά. Αυτή η περίπτωση όµως δεν είναι ϱεαλιστική ούτε µπορεί να εγγυηθεί µια ισορ- ϱοπήµενη ανάπτυξη του δέντρου. Ακόµα όµως και αν καταφέρναµε να δηµιουργήσουµε ένα πλήρες ή σχεδόν πλήρες δυαδικό δέντρο, οι πιθανές διαγραφές και εισαγωγές κόµβων ϑα µπορούσαν να αλλάξουν την µορφή του. είτε στο σχήµα 7.1 όπου και στα δυο δένδρα έχουν εισαχθεί τα ίδια ακριβώς στοιχεία αλλά µε διαφορετική σειρά. Στο δένδρο T a τα στοιχεία έχουν εισαχθεί µε σειρά 1, 2, 3, 4, 5, 6, 7 και στο T b µε σειρά 4, 2, 6, 1, 3, 5, 7. Ο λόγος που το δένδρο T b είναι καλύτερο από το δένδρο T a είναι ότι το T b είναι ισοζυγισµένο ενώ το T a όχι. Τα ισοζυγισµένα δέντρα επιλύουν το παραπάνω πρόβληµα εκτελώντας µετασχηµατισούς (tree rotations) σε κάθε εισαγωγή - διαγραφή κόµβου µε σκοπό την µείωση του ύψους. Η διαδικασίες ανασυγκρότησης των δέντρων αυτού του τύπου έχουν ϕυσικά υπολογιστικό κόστος, το οποίο αντισταθµίζεται από την µείωση του ύψους που επιταχύνει τις υπόλοιπες λειτουργίες. AVL Search Trees Τα ισοζυγισµένα δέντρα κατά ύψος (AVL trees) ανήκουν στην κατηγορία των self-balancing binary search trees και είναι η πρώτη δοµή αυτού του τύπου που επινοήθηκε. Το AVL tree ονοµάστηκε έτσι από τα αρχικά των δηµιουργών του, G.M. Adelson-Velsky & E.M. Landis, που το δηµοσίευασαν το 1962 στην εργασία µε τίτλο An algorithm for the organization of information. Σε ένα AVL tree το ύψος των δυο υποδέντρων οποιουδήποτε κόµβου µπορεί να διαφέρει το πολύ κατά ένα. (Θυµίζουµε ότι ύψος ενός κόµβου, είναι το µήκος του µακρύτερου µονοπατιού από τον κόµβο αυτό µέχρι ένα ϕύλο του δένδρου.) Η αναζήτηση, η εισαγωγή και η διαγραφή απαιτούν χρόνο της 25

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) Σχήµα 7.1: Εκφυλισµένο (T a ) και ισοζυγισµένο (T b ) δυαδικό δέντρο τάξης του O(log n) όπου n είναι ο αριθµός των κόµβων πρίν την εκτέλεση της. Ωστόσο η εισαγωγή και η διαγραφή συνήθως απαιτούν µία η περισσότερες περιστροφές (tree rotations) µε σκοπό την µείωση του ύψους του δέντρου. Ο παράγοντας ισορροπίας (balance factor - bf ) ή ισοζύγιο ενός κόµβου, είναι ο αριθµός που προκύπτει αν αφαιρέσουµε από το ύψος του δεξιού υποδέντρου το ύψος του αριστερού. Ενας κόµβος µε balance factor 1, 0 ή 1 ϑεωρείται ισοζυγισµένος κατά ύψος. Οποιαδήποτε άλλη τιµή χαρακτηρίζει ένα µη ισοζυγισµένο κόµβο, που απαιτεί µετασχηµατισµό του δέντρου. Θυµίζουµε ότι για ένα κόµβο µε αριστερό υποδένδρο Α και δεξιό υποδένδρο Β, το ισοζύγιο είνα Β-Α. Ορισµός 7.1. Ενα δυαδικό δέντρο λέγεται ισοζυγισµένο κατά ύψος ( height balanced tree - AVL tree) (σχήµα 7.2) αν για κάθε κόµβο του δέντρου ικανοποιούνται οι εξής δυο συνθήκες : Ο κόµβος είναι ισοζυγισµένος κατά ύψος (bf 1, 0, 1) Τα δύο υποδέντρα του κόµβου είναι επίσης ισοζυγισµένα κατά ύψος Σχήµα 7.2: Παράδειγµα ισοζυγισµένου δέντρου 26

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) Σχήµα 7.3: Παράδειγµα µη ισοζυγισµένου δέντρου Περιστροφές δέντρου - Tree rotations Μια περιστροφή σε ένα δυαδικό δέντρο αναζήτησης είναι µια διαδικασία η οποία αλλάζει την δοµή του δέντρου χωρίς όµως να επηρεάζει την σειρά των στοιχείων. Χρησιµοποιείται για να αλλάξει την µορφή του δέντρου και συγκεκριµένα µετακινεί µικρότερα υποδέντρα σε χαµηλότερα επίπεδα και µεγαλύτερα υποδέντρα σε υψηλότερα. Υπάρχει µια δυγνωµία στην ϐιβλιογραφία σχετικά µε τον ορισµό και την ονοµατολογία των περιστροφών. Η µια πλευρά υποστηρίζει ότι η κατεύθυνση της περιστροφής άρα και η ονοµατολογία της καθορίζεται από την πλευρά του δέντρου προς την οποία περιστρέφονται οι κόµβοι. Η άλλη πλευρά υποστηρίζει ότι η κατεύθυνση της περιστροφής καθορίζεται από το ποιός κόµβος παίρνει την ϑέση του κόµβου - γονέα. Εµείς ϑα χρησιµοποιήσουµε την πρώτη ερµηνεία (σχήµα 7.4). Μια περιστροφή ονοµάζεται δεξιά περιστροφή όταν οι κόµβοι µετατοπίζονται δεξιόστροφα κατά την ϕορά των δεικτών του ϱολογιού. Η αντίστροφη περιστροφή είναι η αριστερή περιστροφή όπου οι κόµβοι µετατοπίζονται αριστερόστροφα όπως ϕαίνεται στο σχήµα 7.4. Η δεξιά περιστροφή για παράδειγµα µπορεί να περιγραφεί και µε τον ακόλουθο τρόπο : Set P to be the new root. Set P s right child as Q s new left child. Set Q as P s new right child. Οταν ένα δέντρο περιστρέφεται, τότε το υποδέντρο προς το οποίο γίνεται η περιστροφή αυξάνει το ύψος του κατά ένα, ενώ το άλλο υποδέντρο µειώνει το ύψος του κατά ένα. Η λειτουργία αυτή είναι ιδιαίτερα χρήσιµη στα ισοζυγισµένα δέντρα. Εισαγωγή κόµβου σε AVL tree Κατά την είσαγωγή ενός νέου κόµβου, αν το bf κάθε κόµβου είναι 1, 0 ή 1 τότε το δέντρο είναι ισοζυγισµένο και δεν απαιτείται καµία περιστροφή. Αν το bf σε κάποιο κόµβο πάρει τιµή 2 ή 2 τότε το υποδέντρο µε ϱίζα τον κόµβο αυτό δεν είναι ισοζυγισµένο και απαιτείται περιστροφή του δέντρου. Συνήθως µια ή το πολύ δυο περιστροφές είναι ικανές για να επαναφέρουν το δέντρο σε ισοζυγισµένη µορφή. Υπάρχουν συνολικά τέσσερις περιπτώσεις περιστροφής µε τις δυο από αυτές να είναι συµµετρικές των υπολοίπων. Για την διευκόλυνση της περιγραφής ϑα ονοµάζουµε την ϱίζα του µη ισοζυγισµένου υποδέντρου P, το δεξί παιδί του κόµβου αυτού R και το αριστερό L. 27

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) Σχήµα 7.4: εξιά και αριστερή στροφή Αν το bf του κόµβου P είναι 2, αυτό σηµαίνει ότι το δεξί υποδέντρο έχει µεγαλύτερο ύψος από το αριστερό, οπότε και πρέπει να εξεταστεί το ϐφ του δεξιού παιδιού R. Αν το bf του R είναι 1, τότε η εισαγωγή έγινε στην δεξιά πλευρά του R, οπότε απαιτείται µια αριστερή περιστροφή µε ϱίζα τον κόµβο P. Αν το bf του R είναι 1 τότε η εισαγωγή έγινε αριστερά του κόµβου R και απαιτείται διπλή περιστροφή. Η πρώτη είναι µια δεξιά περιστροφή µε ϱίζα το R και η δεύτερη είναι µια αριστερή περιστροφή µε ϱίζα το P. Η άλλες δύο περιπτώσεις προκύπτουν όταν το bf του κόµβου P είναι 2 και είναι συµµετρικές των παραπάνω. Ο ψευδοκώδικας για τα παραπάνω είναι : IF tree is right heavy (bf=2) IF tree s right subtree is left heavy (bf=-1) ELSE Perform Left-Right Rotation Perform Single Left rotation ELSE IF tree is left heavy (bf=-2) IF tree s left subtree is right heavy (bf=1) Perform Right-Left Rotation ELSE Perform Single Right rotation ιαγραφή Αν ο κόµβος, που ϑέλουµε να διαγράψουµε είναι ϕύλλο τότε απλά τον αφαιρούµε από το δέντρο. Στην περίπτωση που δεν είναι ϕύλλο τον αντικαθιστούµε είτε µε τον µεγαλύτερο από το αριστερό του 28

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) υποδέντρο (inorder predecessor) έιτε µε το µικρότερο από το δεξί του υποδέντρο (inorder successor). Ο κόµβος αντικατάστασης έχει το πολύ ένα υποδέντρο. Μετά την διαγραφή ακολουθούµε το µονοπάτι από τον κόµβο αντκατάστασης προς την ϱίζα ανανεώνοντας τους balance factors των κόµβων που συναντάµε. Η διαδικασία αυτή σταµατά αν το bf είναι 1 ή 1 οπότε και το ύψος του υποδέντρου έχει µείνει αµετάβλητο. Αν το bf είναι 0 τότε το ύψος του δέντρου έχει µειωθεί κατά 1 και η διαδικασία πρέπει να συνεχιστεί. Αν το bf είναι 2 ή 2 τότε το υποδέντρο δεν είναι ισοζυγισµένο και πρέπει να περιστραφεί για να επανέλθει σε AVL µορφή. Αν η περιστροφή δώσει bf ίσο µε 0 τότε η διαδικασία retrakcing πρέπει να συνεχιστεί καθώς το ύψος του υποδέντρου έχει µειωθεί κατά 1. Ο υπολογιστικός χρόνος που απαιτείται είναι O(log n) για την αναζήτηση, συν maximum of O(log n) περιστροφές στην διαδικασία re-tracking προς την ϱίζα, οπότε ολόκληρη η διαδικασία απαιτεί χρόνο της τάξης του O(log n). Αναζήτηση Η αναζήτηση σε ένα AVL δέντρο είναι πανοµοιότυπη µε την αναζήτηση σε ένα BS δέντρο. Λόγω του ισοζυγισµένου ύψους του δέντρου ο υπολογιστικός χρόνος που απαιτείται είναι της τάξης του O(log n). Παραδείγµατα περιστροφών Left Rotation (LR) Εστω ότι έχουµε την παρακάτω κατάσταση. a b c Για να επαναφέρουµε το δένδρο σε ισοζυγισµένη κατάσταση πραγµατοποιούµε µια αριστερή περιστροφή στο a. Αυτό γίνεται µε τα ακόλουθα ϐήµατα : 1. το b γίνεται η καινούργια ϱίζα 2. το a παίρνει το αριστερό παιδί του b ως δεξί του παιδί, ή όπως στην περίπτωση µας, το κενό 3. το b παίρνει το a σαν αριστερό του παιδί Και προκύπτει το νέο δένδρο. b a c 29

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) Right Rotation (RR) Είναι το αντίστροφο της αριστερής περιστροφής. Εστω οτι έχουµε την παρακάτω αρχική κατάσταση : c b a Για να το µεταρέψουµε σε ισοζυγισµένο ενεργούµε ανάλογα : 1. το b γίνεται η νέα ϱίζα 2. το c παίρνει το δεξί παιδί του b ως αριστερό παιδί, που στην περίπτωση µας είναι το κενό 3. το b παίρνει το c σαν δεξί του παιδί Και προκύπτει το νέο δένδρο. b a c Left-Right Rotation (LRR) ή Double left Κάποιες ϕορές η απλή αριστερή περιστροφή δεν είναι αρκετή. είτε το παράδειγµα : a c Το δένδρο είναι ισοζυγισµένο. Ας εισάγουµε το b. a c b Τώρα πλέον δεν είναι. Ας δοκιµάσουµε να το διορθώσουµε µε απλή αριστερή περιστροφή. 30

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) c a b Εχουµε καταλήξει στο ίδιο σηµείο. Αν προσπαθήσουµε να δοκιµάσουµε δεξιά περιστροφή σ αυτήν την περίπτωση ϑα καταλήγαµε όπως είχαµε ξεκινήσει. Αυτό είναι αποτέλεσµα του γεγονότος ότι το δεξιό υποδένδρο έχει αρνητικό ισοζύγιο. Επειδή το δεξιό υποδένδρο αφέθηκε να είναι αριστερά ϐαρύ, η περιστροφή µας δεν είχε αποτέλεσµα. Και πως λύνεται Η απάντηση είναι να επιχειρήσουµε µια δεξιά περιστροφή στο δεξιό υποδένδρο. Οχι περιστροφή µε ϐάση την τωρινή ϱίζα αλλά µε ϐάση το δεξί παιδί. Ας αποµονώσουµε το δεξιό υποδένδρο από το υπόλοιπο δένδρο. Εχουµε : c b και µετά τη δεξιά στροφή γύρω από το c: b c Αφού πραγµατοποιήσουµε περιστροφή στο δεξί µας υποδένδρο, έχουµε προετοιµάσει την ϱίζα µας για αριστερή περιστροφή. Ετσι είναι τώρα το δένδρο µας. a b c 31

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) Φαίνεται πλέον καθαρά ότι είµαστε έτοιµοι για αριστερή περιστροφή : b a c Right-Left Rotation (RLR) ή Double right Μια δεξιά-αριστερή περιστροφή είναι η περιστροφή που χρειαζόµαστε όταν επιχειρούµε να ισοζυγίσουµε ένα δένδρο που το αριστερό υποδένδρο είναι δεξιά ϐαρύ. Ας δούµε ένα παράδειγµα πότε την χρειαζό- µαστε : c a b Το δένδρο είναι µη ισοζυγισµένο. Το αριστερό υποδένδρο έχει ύψος 2 και το δεξί υποδένδρο έχει ύψος 0. Άρα το ισοζύγιο της ϱίζας είναι 2. Μια απλή δεξιά περιστροφή δεν ϑα έλυνε το πρόβληµα. Ας τη δοκιµάσουµε : a c b Προφανώς δεν ϐοήθησε. Απλά τώρα έχουµε ένα δένδρο µε ισοζύγιο 2. Ο λόγος που δεν δούλεψε η δεξιά περιστροφή είναι γιατί το αριστερό υποδένδρο, δηλ το a, έχει ϑετικό ισοζύγιο και άρα είναι δεξιά ϐαρύ. Η λύση είναι να κάνουµε το αριστερό υποδένδρο µας αριστερά ϐαρύ. Το πετυχαίνουµε εκτελώντας αριστερή περιστροφή στο αριστερό υποδένδρο. Αν το επιχειρήσουµε αυτό καταλήγουµε έτσι : 32

ΕΡΓΑΣΤΗΡΙΟ 7. ΙΣΟΖΥΓΙΣΜΕΝΟ ΕΝΤΡΟ (AVL TREE) c b a Αυτό όµως το δένδρο µπορούµε να το ισοζυγίσουµε εκτελώντας µια απλή δεξιά περιστροφή. Την επιχειρούµε ξεκινώντας µε ϱίζα το c και καταλήγουµε έτσι : b a c Links Animated AVL trees: http://www.cs.jhu.edu/ goodrich/dsa/trees/avltree.html http://webpages.ull.es/users/jriera/docencia/avl/avl%20tree%20applet.htm Wikipedia page for Tree Rotations http://en.wikipedia.org/wiki/tree_rotation Ασκηση 7.1 Κατασκευάστε ένα ισοζυγισµένο δέντρο αναζήτησης µε ϐάση τον κώδικα και την ϕιλοσοφία κατασκευής του δυαδικού δέντρου αναζήτησης που παρουσιάσαµε σε προηγούµενο εργαστήριο. Σηµείωση: Ακολουθώντας µια bottom up προσσέγγιση µπορείτε να ξεκινήσετε κατασκευάζοντας αρχικά τον κόµβο του δέντρου που είναι ουσιαστικά ένα TreeNode µε επιπλέον χαρακτηριστικά. Στην συνέχεια µπορείτε να προχωρήσετε στην κατασκευή του ισοζυγισµένου δέντρου που είναι ένα BSTree µε επιπλέον µεθόδους για τις περιστροφές. Γράφοντας τον κώδικα για τις περιστροφές µπορείτε στην συνέχεια να τις ενσωµατώσετε στις µεθόδους εισαγωγής και διαγραφής. 33

Εργαστήριο 8 Σωρός (Heap) Εισαγωγή Ενα δυαδικό δέντρο ϐάθους N ονοµάζεται πλήρες ( complete), όταν έχει όλους τους κόµβους του επιπέδου N συµπληρωµένους. Ενα δυαδικό δέντρο ϐάθους N ονοµάζεται σχεδόν πλήρες (almost complete), όταν έχει όλους τους κόµβους του επιπέδου N 1 συµπληρωµένους και οι κόµβοι που υπάρχουν στο N-οστό επίπεδο είναι τοποθετηµένοι όσο το δυνατόν πιο αριστερά. Η τοποθέτηση των κόµβων σε ένα σχεδόν πλήρες δυαδικό δέντρο είναι τέτοια που µας επιτρέπει να αριθµήσουµε µε ένα συστηµατικό τρόπο τους κόµβους αυτούς. Αυτή η συστηµατική αρίθµηση (από πάνω προς τα κάτω και από αριστερά προς τα δεξιά κάνει δυνατή την αναπαράσταση ενός σχεδόν πλήρους δυαδικού δέντρου µε τη ϐοήθεια ενός απλού γραµµικού πίνακα, όπως ϕαίνεται στο σχήµα 8.1. (Θεωρούµε ότι το στοιχείο µε ενδείκτη 0 του πίνακα δεν χρησιµοποιείται). Η ϱίζα του σωρού ϐρίσκεται στη ϑέση array[1] ενώ το αριστερό και δεξιό παιδί ενός στοιχείου στη ϑέση i ϐρίσκονται στις ϑέσεις 2i και 2i + 1 αντίστοιχα. Ο γονέας του κόµβου j είναι ο j/2 (ακέραια διαίρεση). Το εργαστήριο ασχολείται µε την κατασκευή κώδικα για σωρούς (Heap) και ειδικότερα δυαδικούς σωρούς. Ο δυαδικός σωρός είναι µια δοµή δεδοµένων η οποία µπορεί να ϑεωρηθεί ώς ένα δυαδικό δένδρο µε δύο επιπλέον ιδιότητες : Ολα τα επίπεδα του δένδρου εκτός ίσως από το χαµηλότερο είναι πλήρη και αν το τελευταίο επίπεδο δεν είναι πλήρες τότε οι κόµβοι εισάγονται από αριστερά προς τα δεξιά. Τα δεδοµένα κάθε κόµβου είναι µεγαλύτερα ( max heap) (σχήµα 8.2β ) ή µικρότερα (min heap)(σχήµα 8.2α ) από αυτά των παιδιών του. Οι ϐασικές πράξεις που ορίζονται για τη δοµή δεδοµένων σωρός, είναι η πράξη της εισαγωγής και η πράξη της διαγραφής ενός στοιχείου. Εισαγωγή στοιχείου (insert) Η πράξη της εισαγωγής ϕροντίζει ώστε ο σωρός που προκύπτει να έχει το µικρότερο (ή µεγαλύτερο για maxheap) στοιχείο του πάντοτε στην ϱίζα του δέντρου (κορυφή του σωρού). Στο επόµενο σχήµα ϕαίνεται µία σειρά από διαδοχικές πράξεις εισαγωγής στοιχείων οι οποίες δηµιουργούν ένα σωρό. MinHeap 1. Οταν εισάγουµε ένα καινούργιο στοιχείο στο σωρό αυτό τοποθετείται στην επόµενη διαθέσιµη ϑέση στο τελευταίο επίπεδο (τελευταία ϑέση του πίνακα). 34

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) 1 D 2 3 A C 4 5 6 7 B F E G 8 9 10 H I J D A C B F E G H I J 1 2 3 4 5 6 7 8 9 10 Σχήµα 8.1: Αναπαράσταση σχεδόν πλήρους δυαδικού δέντρου µε πίνακα (α ) Min Heap (ϐ ) Max Heap Σχήµα 8.2 2. Αν η τιµή του στοιχείου δεν παραβιάζει την ιδιότητα του σωρού (δηλαδή η τιµή του είναι µεγαλύτερη από αυτή του γονέα του) τότε το στοιχείο µένει στη ϑέση του. 3. Σε διαφορετική περίπτωση παιδί και γονέας ανταλλάσσουν ϑέσεις. Αυτή η διαδικασία συνεχίζεται µέχρι το καινούργιο στοιχείο να ϐρει µια ϑέση τέτοια ώστε να ικανοποιείται η ιδιότητα του σωρού. Ετσι κάθε στοιχείο που εισάγεται στο σωρό ϑα πρέπει να διασχίσει ένα µονοπάτι από το ϕύλλο στο οποίο ϐρισκόταν αρχικά µέχρι κάποιον κατάλληλο εσωτερικό κόµβο. Στη χειρότερη περίπτωση το µονοπάτι ϑα είναι ως τη ϱίζα του δέντρου. Η διαδικασία αυτή ονοµάζεται percolate up ή shift up (σχήµα 8.3). 35

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) Αντί για διαδοχικά swap, είναι πιό αποδοτικό να χρησιµοποιήσουµε µία τρύπα (έναν άδειο κόµβο), ο οποίος ϑα κάνει τη διαδικασία percolate up. Στο σηµείο που ϑα σταµατήσει η τρύπα (ικανοποιείται η ιδιότητα του σωρού) ϑα εισάγουµε το στοιχείο (σχήµα 8.4). Ψευδοκώδικας για την εισαγωγή νέου στοιχείου στο (minheap). if (heap is full ) terminate currentsize=currentsize+1 hole=currentsize while (hole>1 and x<array[hole/2]) array[hole]=array[hole/2] hole=hole/2 end while array[hole]=x 13 13 21 16 Insert 18 21 16 24 31 19 68 24 31 19 68 65 26 32 65 26 32 18 Percolate up 13 13 18 16 Percolate up 21 16 24 21 19 68 24 18 19 68 65 26 32 31 65 26 32 31 Σχήµα 8.3: ιαδικασία εισαγωγής στοιχείων σε ένα σωρό (min Heap) ιαγραφή στοιχείου (remove) Η πράξη διαγραφής αφαιρεί πάντοτε το στοιχείο που ϐρίσκεται στην κορυφή και ϕροντίζει να αναδιατάσσει το σωρό, έτσι ώστε το αµέσως µικρότερο (ή µεγαλύτερο για maxheap) στοιχείο να ϐρεθεί στην κορυφή του σωρού. Η ϱίζα του σωρού εξάγεται και ο σωρός τακτοποιείται ως εξής (σχήµα 8.5): 36

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) p p < e p e e hole p > e e Σχήµα 8.4: Εισαγωγή στοιχείου σε min Heap µε τη χρήση τρύπας p Η ϱίζα ανταλλάσσεται µε το τελευταίο ϕύλλο. Η νέα ϱίζα συγκρίνεται µε κάθε παιδί της και αν ένα από αυτά είναι µικρότερο από τη ϱίζα, τότε το ανταλλάσουµε µε αυτή. Συνεχίζουµε τις συγκρίσεις/ανταλλαγές µε τα παιδιά της νέας ϱίζας µέχρι να ϕθάσει σε ένα επίπεδο του δένδρου όπου ϑα είναι µεγαλύτερη από τα αντίστοιχα παιδιά Η διαδικασία ονοµάζεται percolate down ή shift down. Οπως και στη διαδικασία της εισαγωγής είναι προτιµότερο αντι για διαδοχικά swap, να χρησι- µοποιήσουµς µια τρύπα (έναν άδειο κόµβο) για τη διαδικασία percolate down (σχήµα 8.6): ηµιουργείται µια τρύπα στη ϱίζα, διαγράφοντας το στοιχείο της ϱίζας Αντιγράφουµε το τελευταίο στοιχείο σε µια προσωρινή µεταβλητή και διαγράφουµε το τελευταίο στοιχείο. Οσο δεν ισχύει η ιδιότητα του σωρού µετακινούµε την τρύπα προς τα κάτω. Στο σηµείου που ϑα σταµατήσει η τρύπα (ικανοποιείται η συνθήκη του σωρού), εισάγουµε το στοιχείο από την προσωρινή µεταβλητή. Heap.java public interface Heap extends DataStructure { //insert item x in the heap public void insert ( Comparable x ) ; // return and remove the smallest item public Comparable deletemin ( ) throws StructureEmptyException ; // return the smallest item public Comparable findmin ( ) throws StructureEmptyException ; } 37

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) 13 Delete min element 31 18 19 delete last node 18 19 24 21 23 68 24 21 23 68 65 26 32 31 65 26 32 Replace by value of last node 18 Percolate down 18 21 19 Percolate down 31 19 24 31 23 68 24 21 23 68 65 26 32 65 26 32 Σχήµα 8.5: ιαγραφή στοιχείου από min Heap Ασκηση 8.1 Χρησιµοποιώντας το Heap interface κατασκευάστε µία κλάση ArrayMinHeap, η οποία ϑα υ- λοποιεί ένα min Heap χρησιµοποιώντας ως αποθηκευτικό χώρο έναν πίνακα. Η ϱίζα του σωρού ϑα ϐρίσκεται στη ϑέση 1 του πίνακα (σχήµα 8.1). Κατασκευάστε τρείς δοµητές : 1. έναν default, public ArrayMinHeap( ) 2. και δύο παραµετρικούς public ArrayMinHeap( int capacity ) public ArrayMinHeap( Comparable [ ] items ) Σε κάθε κλάση, που δηµιουργείτε καλό είναι να υλοποιείτε και µια µέθοδο public String tostring(), ώστε να είναι πιό εύκολες οι εκτυπώσεις και τελικά η δοκιµή του κώδικα, που γράφετε. Ασκηση 8.2 Να γράψετε µέθοδο public Comparable[ ] HeapSort(Comparable[ ] inarray) η οποία να παίρνει ως είσοδο έναν πίνακα και να τον επιστρέφει ταξινοµηµένο µε τη µέθοδο HeapSort Για τη µετατροπή ενός πίνακα σε σωρό µπορείτε να χρησιµοποιήσετε τον αλγόριθµο HeapBottomUp (σχήµα 8.7, σχήµα 8.8) 38

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) e < min(l, r) l r e > l > r e e > l < r r l r l l r Σχήµα 8.6: ιαγραφή στοιχείου από min Heap µε τη χρήση τρύπας Βήµα 1 Ξεκινήστε άπό το επίπεδο που περιέχει τον τελευταίο κόµβο που δεν είναι ϕύλλο (array[n/2], όπου n το µέγεθος του σωρού ). Βήµα 2 Μετατρέψτε το υπο-δέντρο, που ξεκινάει από τον παραπάνω κόµβο, σε σωρό, χρησιµοποιώντας τη µέθοδο percolatedown. Βήµα 3 Μετακινηθείτε στο ίδιο επίπεδο πρός τα αριστερά και µετατρέψτε το υποδένδτρο, που έχει σαν ϱίζα τον αντίστοιχο κόµβο, σε σωρό, χρησιµοποιώντας τη µέθοδο percolatedown. Βήµα 4 Αν τα επίπεδα δεν έχουν τελειώσει, µετακινηθείτε σε χαµηλότερο επίπεδο και επαναλάβατε από το ϐήµα 3. Βήµα 5 Τέλος. 70 29 68 65 32 19 16 13 26 31 At each stage convert the highlighted tree into a MinHeap by percolating down starting at the root of the highlighted tree. 70 29 68 65 32 19 16 13 26 31 70 29 68 65 31 19 16 13 26 32 70 29 68 13 31 19 16 65 26 32 26 13 16 13 70 16 29 70 16 29 31 19 68 26 31 19 68 13 31 19 68 65 70 32 65 29 32 65 26 32 Σχήµα 8.7: Μετατροπή πίνακα σε min Heap 39

ΕΡΓΑΣΤΗΡΙΟ 8. ΣΩΡΟΣ (HEAP) Σχήµα 8.8: Αλγόριθµος BottomUp 40

Εργαστήριο 9 Πίνακας Κατακερµατισµού (Hash Table) Εισαγωγή Ενα σηµαντικό ϑέµα στη ϑεωρία των δοµών δεδοµένων και των αλγορίθµων είναι η αποτελεσ- µατική αποθήκευση και ανάκτηση των δεδοµένων. Στην περίπτωση της τυχαίας (ή σειριακής) τοπο- ϑέτησης, όλες οι εργασίες (εισαγωγή, αναζήτηση, διαγραφή κλπ) απαιτούν χρόνο ανάλογο µε το πλήθος των αποθηκευµένων δεδοµένων (O(n), όπου n το πλήθος των αποθηκευµένων δεδοµένων). Η κατάσταση ϐελτιώνεται όταν τα δεδοµένα αποθηκευτούν µε κάποιο δοµηµένο τρόπο. Για παράδειγµα, στην περίπτωση που χρησιµοποιούµε το δυαδικό δένδρο αναζήτησης και µάλιστα όταν αυτό είναι ισορροπηµένο (AVL tree) ο χρόνος ϐελτιώνεται στο O(log n). Ο στόχος των Hash Tables είναι να επιτευχθεί σταθερός χρόνος πρόσβασης, ανεξάρτητος από το πλήθος των δεδοµένων. Ας υποθέσουµε οτι είναι δυνατόν να υπολογίσουµε τη ϑέση (διεύθυνση στη µνήµη, ή ϑέση σε έναν πίνακα), στην οποία ϑα έπρεπε να ϐρίσκεται ενα αντικείµενο αν ήταν αποθηκευµένο στη δοµή. Υποθέτοντας οτι µπορούµε να κάνουµε τον υπολογισµό αυτό ανεξάρτητα από το πλήθος των αποθηκευµένων στοιχείων, έχουµε επιτύχει να έχουµε πρόσβαση στα δεδοµένα σε σταθερό χρόνο. Ας ϑεωρήσουµε οτι τα δεδοµένα µας είναι αποθηκευµένα σε έναν πίνακα (σχήµα 9.1). Σύµφωνα µε το παραπάνω σκεπτικό ϑα ήταν αρκετό αν µπορούσαµε να υπολογίσουµε την ϑέση του πίνακα στην οποία ϑα ήταν αποθηκευµένο το κάθε δεδοµένο. Ενα σηµαντικό ερώτηµα είναι, πώς ένα πλήθος από κλειδιά ϑα µετασχηµατισθεί σε ένα πλήθος από ϑέσεις πίνακα. Αναζητούµε λοιπόν µία συνάρτηση, που να υπολογίζει για κάθε αντικείµενο έναν ακέραιο αριθµό, ο οποίος ϑα είναι και η αντίστοιχη ϑέση του πίνακα. Ο ακέραιος αυτός υπολογίζεται από τα δεδοµένα του αντικειµένου και ονοµάζεται hash code και η πράξη κατακερµατισµός (hashing ή lookup). Σε ένα πίνακα κατακερµατισµού αυτό επιτυγχάνεται µε µία συνάρτηση κατακερµατισµού (hashing function). Ο πίνακας ονοµάζεται hash table. Τα hash tables υποστηρίζουν πρόσβαση στη δοµή κατά µέσο όρο σε O(1), υπό την προϋπόθεση οτι το πλήθος των αποθηκευµένων αντικειµένων δεν ξεπερνά ένα µέγιστο παράγοντα ϕόρτου (load factor). Ορισµός 9.1. Ως load factor ορίζουµε το λόγο n/s των αποθηκευµένων στοιχείων σε ένα hash table (n), προς το µέγεθος του πίνακα s. Για κάποια είδη κλειδιών δεν απαιτείται η συνάρτηση κατακερµατισµού. Οι τιµές των κλειδιών µπορούν κατευθείαν να χρησιµοποιηθούν ως ϑέσεις πίνακα. Ας εξετάσουµε αυτή την απλή περίπτωση πρώτα : Εστω ένα πρόγραµµα µε σκοπό την προσπέλαση εγγραφών εργαζοµένων για µια εταιρεία, µε π.χ. 1000 εργαζόµενους. Κάθε εγγραφή εργαζόµενου απαιτεί, έστω, 1000 bytes αποθηκευτικό χώρο. Ετσι µπορούµε να αποθηκεύσουµε όλη τη ϐάση δεδοµένων σε 1 megabyte, που άνετα χωρά στη µνήµη του υπολογιστή µας. Στον κάθε εργαζόµενο έχει δοθεί ένας αριθµός µητρώου από το 1 έως το 1000. Αυτοί οι αριθµοί µητρώου µπορούν να χρησιµοποιηθούν σαν κλειδιά για την προσπέλαση των εγγραφών. 41

ΕΡΓΑΣΤΗΡΙΟ 9. ΠΙΝΑΚΑΣ ΚΑΤΑΚΕΡΜΑΤΙΣΜΟΥ (HASH TABLE) (α ) Hashing - Hash Table (ϐ ) Ενας τηλεφωνικός κατάλογος ως hash table Σχήµα 9.1 Εποµένως, για την αποθήκευση των πληροφοριών, ϑα επιλέγαµε ένα πίνακα 1000 ϑέσεων, όπου η κάθε ϑέση του πίνακα αντιπροσωπεύει έναν αντίστοιχο αριθµό µητρώου ενός εργαζόµενου. Άρα, όταν µας ενδιαφέρει κάποιος εργαζόµενος, µέσω του αριθµού µητρώου γίνεται άµεση προσπέλαση στον πίνακα χωρίς να χρειάζεται κάποιο είδος αναζήτησης. Επίσης, η εισαγωγή µιας νέας εγγραφής επιτυγχάνεται εύκολα και γρήγορα, π.χ. δίνεται ο αριθµός µητρώου 1001 σε ένα νέο εργαζόµενο και τοποθετείται στη ϑέση 1001 του πίνακα, ο οποίος ϐέβαια ϑα πρέπει πρώτα να διευρυνθεί. Πολλές ϕορές, όµως, τα κλειδιά των εγγραφών δεν αποτελούν συνεχόµενη ακολουθία ακεραίων ή µπορεί να µην είναι καν αριθµητικά. Στην περίπτωση που τα κλειδιά δεν είναι αριθµητικά, χρησι- µοποιούµε τις ASCII τιµές των χαρακτήρων τους τις οποίες µετά προσθέτουµε για να τα µετατρέψουµε σε αριθµητικά. Και πάλι όµως έχουµε να τοποθετήσουµε αριθµητικά κλειδιά µε µεγάλες διαφορές µεταξύ τους σε ένα πίνακα µε συνεχόµενες ϑέσεις. Π.χ. έστω ότι έχουµε 10 κλειδιά στο διάστηµα 0 199. Χρειαζόµαστε ένα πίνακα 10 ϑέσεων µε δείκτες 0 9. Μία καλή λύση είναι να διαιρούµε τα κλειδιά µε το 10, που είναι το µέγεθος του πίνακα και να παίρνουµε το υπόλοιπο της διαίρεσης. Για παράδειγµα, η εγγραφή µε κλειδί 13 ϑα αποθηκευτεί στη ϑέση 13 % 10 = 3. Μία παρόµοια έκφραση µπορεί χρησιµοποιηθεί για να ϐρεθεί η ϑέση του πίνακα για οποιοδήποτε κλειδί. Θέση πίνακα = αρχικό κλειδί % µέγεθος πίνακα Αυτό είναι ένα παράδειγµα µίας συνάρτησης κατακερµατισµού. Μετασχηµατίζει ένα µεγάλο διάστηµα τιµών σε ένα µικρότερο διάστηµα. Ενας πίνακας στον οποίο εισάγονται δεδοµένα µε µία τέτοια συνάρτηση µετασχηµατισµού του κλειδιού ονοµάζεται πίνακας κατακερµατισµού (hash table). ΣΥΓΚΡΟΥΣΕΙΣ (Collisions) Ο προηγούµενος µετασχηµατισµός δεν εγγυάται πάντα ότι δύο διαφορετικά κλειδιά δεν ϑα οδηγηθούν στην ίδια ϑέση του πίνακα. Για παράδειγµα, οι εγγραφές µε κλειδιά 13 και 23 προκύπτει ότι ϑα αποθηκευτούν στη ϑέση 3. Στην ιδανική περίπτωση διαφορετικά αντικείµενα ϑα πρέπει να αντιστοιχούν σε διαφορετικά hash codes. Αυτό είναι ϑεωρητικά εφικτό, όταν ο αποθηκευτικός χώρος είναι απεριόριστος αλλά είναι µη ϱεαλιστικό στην πράξη, που έχουµε πεπερασµένο χώρο αποθήκευσης. Για παράδειγµα αν ϑέλαµε να αποθηκεύσουµε strings µέγιστου µεγέθους 16 χαρακτήρων και έστω οτι χρησιµοποιούµε µόνο τους πεζούς (a... z), το πλήθος των δυνατών strings ϑα ήταν : 26 16 = 43608742899428874059776 και άρα χρειαζόµαστε αποθηκευτικό χώρο αντίστοιχου µεγέθους. Στην πράξη ϐέβαια δεν έχουµε στη διάθεσή 42

ΕΡΓΑΣΤΗΡΙΟ 9. ΠΙΝΑΚΑΣ ΚΑΤΑΚΕΡΜΑΤΙΣΜΟΥ (HASH TABLE) Σχήµα 9.2: Επίλυση µε γραµµική διερεύνηση µας τέτοια µεγέθη και επίσης οι hash functions δεν είναι πάντα ιδανικές, οπότε δηµιουργείται ο ίδιος hash code για διαφορετικά κλειδιά (collision). Το πρόβληµα διευθετείται µε διαδικασίες επίλυσης συγκρούσεων (collision resolution). Οι δύο εγγραφές που διεκδικούν την ίδια ϑέση στον πίνακα ονοµάζονται συνώνυµα (synonyms). Οι πιό δηµοφιλείς µέθοδοι επίλυσης συγγρούσεων, ανήκουν στη κατηγορία της ανοικτής διευθυνσιοδότησης (open addressing) και της χωριστής αλυσίδωσης (separate chaining). Μία προσέγγιση για τη λύση στο πρόβληµα αυτό, είναι να εξετάσουµε τον πίνακα, να ϐρούµε µία άδεια ϑέση και να εισάγουµε εκεί το νέο στοιχείο. Αυτή η τεχνική ονοµάζεται ανοιχτή διευ- ϑυνσιοδότηση (open addressing). Οι διαδικασίες της ανοικτής διευθυνσιοδότησης αποθηκεύουν τις εγγραφές απευθείας στον πίνακα. Η σύγκρουση επιλύεται µε διερεύνηση (probing) δηλ. αναζήτηση σε εναλλακτικές διευθύνσεις του πίνακα µέχρι να ϐρεθεί η εγγραφή που αναζητούµε ή µέχρι να ϕθάσουµε σε άδεια ϑέση, που σηµαίνει οτι δεν υπάρχει το κλειδί στον πίνακα. Οι διαδικασίες διερεύνησης εναλλακτικών διευθύνσεων περιλαµβάνουν : Γραµµική διερεύνηση (linear probing), στην οποία το διάστηµα µεταξύ των ϑέσεων είναι σταθερό (συνήθως 1) (σχήµα 9.2). Τετραγωνική διερεύνηση (quadratic probing) στην οποία το διάστηµα αυξάνει ανάλογα µε το τετράγωνο ενός αριθµού (probe number) ιπλός κατακερµατισµός (double hashing) στην οποία το διάστηµα υπολογίζεται από µία άλλη hash function. Μία δεύτερη προσέγγιση είναι η δηµιουργία ενός πίνακα που αποτελείται από συνδεδεµένες λίστες. Ετσι, όταν συµβεί σύγκρουση, το νέο στοιχείο εισάγεται στη λίστα της συγκεκριµένης ϑέσης του πίνακα. Αυτή η τεχνική ονοµάζεται χωριστή αλυσίδωση (separate chaining) (σχήµα 9.3). Στην περίπτωση της χωριστής αλυσίδωσης, κάθε ϑέση του πίνακα αντιστοιχεί σε µία συνδεδεµένη λίστα (ή το πρώτο στοιχείο µιάς συνδεδεµένης λίστας), η οποία περιέχει τα στοιχεία, τα οποία η hash function αντιστοίχισε στην ίδια διεύθυνση. Κατά τη διαδικασία της εισαγωγής ϐρίσκουµε τη σωστή 43

ΕΡΓΑΣΤΗΡΙΟ 9. ΠΙΝΑΚΑΣ ΚΑΤΑΚΕΡΜΑΤΙΣΜΟΥ (HASH TABLE) Σχήµα 9.3: Επίλυση σύγκρουσης µε χωριστή αλυσίδωση διεύθυνση και προσθέτουµε το αντικείµενο στο τέλος της λίστας. Η διαγραφή γίνεται διαγράφοντας το αντικείµενο από την αντίστοιχη λίστα. Το µέγεθος του πίνακα είναι συνήθως ένας πρώτος αριθµός. Η απόδοση των µεθόδων επίλυσης δεν εξαρτάται από τον αριθµό των αποθηκευµένων αντικειµένων αλλά από το (load factor n/s). Με τη χρήση µια καλής συνάρτησης κατακερµατισµού, η αναζήτηση είναι σχεδόν σταθερού χρόνου µε τιµές του παράγοντα ϕόρτου από 0 έως 0.7. µε τιµές πάνω από 0.7 η πιθανότητα συγκρούσεων και το κόστος διαχείρησής τους είναι ασύµφορα µεγάλο. Στό άλλο όριο, όταν το load factor πλησιάζει το 0, η ανάγκη για µνήµη γίνεται πολύ µεγάλη, χωρίς ουσιαστικό κέρδος στο χρόνο αναζήτησης. Μια συνήθης τιµή για το load factor είναι 0.7. Παράδειγµα Εστω ότι έχουµε τα κλειδιά 33, 41, 53, 74, 87, 127, 144, 154, 178, 199 και ϑέλουµε να τα αποθηκεύσουµε σε ένα πίνακα 10 ϑέσεων µε ϑέσεις 0 9. Χρησιµοποιώντας την µέθοδο της διαίρεσης των κλειδιών µε το 10 και παίρνοντας το υπόλοιπο ως τη ϑέση στον πίνακα, παρατηρούµε ότι τα κλειδιά 74, 144, 154 είναι συνώνυµα. Το ίδιο και τα 33, 53 καθώς επίσης και τα 87, 127. Αν χρησιµοποιήσουµε την προηγούµενη µέθοδο ϑα έχουµε µια καταστροφικά αναποτελεσµατική δοµή αποθήκευσης. Για το λόγο αυτό ϑα επιλέξουµε την τεχνική της ξεχωριστής αλυσίδωσης. Οταν γίνει η αποθήκευση των κλειδιών ϑα έχουµε την εξής κατάσταση : 0 null 1 41 2 null 3 33 53 4 74 144 54 5 null 6 null 7 87 127 8 178 9 199 44

ΕΡΓΑΣΤΗΡΙΟ 9. ΠΙΝΑΚΑΣ ΚΑΤΑΚΕΡΜΑΤΙΣΜΟΥ (HASH TABLE) Ασκηση 9.1 Ακολουθώντας το interface HashTable κατασκευάστε µία κλάση SeparateChainingHashTable, που υλοποιεί έναν hash table, ο οποίος επιλύει τις συγκρούσεις µε τη µέθοδο της χωριστής αλυσίδωσης. Η κλάση SeparateChainingHashTable ϑα περιλαµβάνει έναν πίνακα του οποίου τα στοιχεία ϑα είναι απλά συνδεδεµένες λίστες. Η εισαγωγή ενός αντικειµένου ϑα γίνεται εισάγοντας το στη λίστα που ϐρίσκεται στη ϑέση που υποδεικνύει ο hash code του αντικειµένου που ϑέλουµε να εισάγουµε. Για τον υπολογισµό του hash code του αντικειµένου χρησιµοποιείστε τη µέθοδο int hashcode() της κλάσης Object της Java (http://java.sun.com/j2se/1.4.2/docs/api/java/lang/object.html) HashTable.java public interface HashTable extends DataStructure { public void insert ( Object x ) ; } public void remove ( Object x ) ; 45

Εργαστήριο 10 Γράφηµα (Graph) Εισαγωγή Στην πληροφορική γράφηµα ονοµάζεται µια δοµή δεδοµένων, που αποτελείται από ένα σύνολο κορυφών ( vertices) (ή κόµβων ( nodes» και ένα σύνολο ακµών ( edges). Ενας επίσηµος ορισµός είναι ο ακόλουθος : Ορισµός 10.1. Εστω το µη κενό και πεπερασµένο σύνολο V µε n διακεκριµένα στοιχεία V = {v 1,..., v n }, και E ένα σύνολο µε m 0 µη διατεταγµένα Ϲεύγη e ij = {v i, v j }, i j, στοιχείων του V. Τότε το διατεταγµένο Ϲεύγος G = (V, E) ονοµάζεται µη κατευθυνόµενο γράφηµα ( undirected graph) ή απλώς γράφηµα. Κάθε στοιχείο v i του V είναι µία κορυφή και κάθε στοιχείο e ij του E είναι µία ακµή. Παρατηρούµε ότι για m = 0 το σύνολο E των ακµών είναι κενό, δηλαδή ένα γράφηµα µπορεί να αποτελείται µόνο από κορυφές. Τάξη ( order) ενός γραφήµατος ονοµάζουµε το πλήθος των κορυφών του (δηλαδή το n), ενώ µέγεθος ( size) ονοµάζουµε το πλήθος των ακµών του (δηλαδή το m). Ενα γράφηµα µπορεί να παρασταθεί γεωµετρικά µε ένα διάγραµµα, και υπάρχουν άπειροι τρόποι να απεικονιστεί, αλλάζοντας απλά την ϑέση των κορυφών στο επίπεδο. Μία περίπτωση απεικόνισης του γραφήµατος G(V, E) µε V = {α, β, γ, δ, ɛ} και Σχήµα 10.1: Παράδειγµα E = {{α, β}, {α, ɛ}, {β, γ}, {β, ɛ}, {γ, δ}, {δ, ɛ}} ϕαίνεται στο σχή- µα 10.1. ύο κορυφές v i, v j ονοµάζονται διπλανές (adjacent) όταν υπάρχει ακµή e ij = {v i, v j } που να τις έχει άκρα. ύο κορυφές που δεν είναι γραφήµατος διπλανές ονοµάζονται ανεξάρτητες ( independent). Ετσι στο σχήµα 10.1 η κορυφές α και ɛ είναι διπλανές ενώ οι β και δ ανεξάρτητες. Βαθµό ( degree) µιας κορυφής ονοµάζουµε το πλήθος των διπλανών κορυφών της, ή αλλιώς το πλήθος των ακµών που πρόσκεινται στην κορυφή. Ετσι στο ίδιο σχήµα 10.1 ο ϐαθµός της κορυφής α είναι 2, και γράφεται deg(α) = 2, ενώ της β είναι 3. Μια κορυφή ονοµάζεται άρτια ή περιττή ανάλογα µε το αν ο ϐαθµός της είναι άρτιος ή περιττός. Μία κορυφή v i µε ϐαθµό deg(v i ) = 0 ονοµάζεται µεµονωµένη (isolated), ενώ αν deg(v i ) = 1 η v i ονοµάζεται τελική κορυφή (end vertex). Εάν ένα γράφηµα περιλαµβάνει όλους τους δυνατούς συνδυασµούς ακµών ανάµεσα στις κορυφές του, τότε ονοµάζεται πλήρες γράφηµα ( complete graph). Στην συνέχεια δίνουµε ορισµένους περαιτέρω ορισµούς γύρω από την έννοια του γραφήµατος. Ενα γράφηµα U είναι υπογράφηµα ( subgraph) ενός γραφήµατος G, όταν όλες οι κορυφές και ακµές του είναι κορυφές και ακµές του G. Το G ονοµάζεται υπεργράφος (supergraph) του U. 46