Εργαστήριο 7 Ισοζυγισµένο έντρο (AVL Tree) Εισαγωγή Εκτός από τα δυαδικά δέντρα αναζήτησης (inry serh trees) που εξετάσαµε σε προηγούµενο εργαστήριο, υπάρχουν αρκετά είδη δέντρων αναζήτησης µε ξεχωριστό ενδιαφέρον. Τα ισοζυγισµένα δέντρα αναζήτησης (self-lning inry serh tree) είναι δυαδικά δέντρα αναζήτησης, τα οποία κρατούν το ύψος τους όσο το δυνατό µικρότερο, µέσω αυτόµατων διαδικασιών αναπροσαρµογής των κόµβων τους. Οι περισσότερες λειτουργίες, που εκτελούνται σε ένα δυαδικό δέντρο αναζήτησης, α- παιτούν υπολογιστικό χρόνο ευθέως ανάλογο µε το ύψος του δέντρου. Είναι λοιπόν προφανές ότι µας ενδιαφέρει να παραµένει το ύψος του δέντρου όσο το δυνατό µικρότερο. Τα απλά BS Trees έχουν το µειονέκτηµα της γρήγορης ανάπτυξης ύψους σε σχετικά συνηθησµένες καταστάσεις. Για παράδειγµα, αν εισάγουµε ταξινοµηµένους αριθµούς σε ένα απλό BS Tree ϑα καταλήξουµε σε ένα δέντρο-κλαδί µε ύψος όσο και το πλήθος των εισαγόµενων κόµβων (σχήµα 7.1). Σε αυτή την περίπτωση η αναζήτηση, για παράδειγµα (στην οποία και πλεονεκτεί το BS Tree), ϑα χρειαστεί τον ίδιο χρόνο, που απαιτείται και σε µια λίστα. Αν γνωρίζαµε τα εισαγόµενα στοιχεία εκ τον προτέρων ϑα µπορούσαµε να κρατήσουµε την ανάπτυξη του δέντρου σε χαµηλό ύψος εισάγοντας τα στοιχεία σε τυχαία σειρά. Αυτή η περίπτωση όµως δεν είναι ϱεαλιστική ούτε µπορεί να εγγυηθεί µια ισορ- ϱοπήµενη ανάπτυξη του δέντρου. Ακόµα όµως και αν καταφέρναµε να δηµιουργήσουµε ένα πλήρες ή σχεδόν πλήρες δυαδικό δέντρο, οι πιθανές διαγραφές και εισαγωγές κόµβων ϑα µπορούσαν να αλλάξουν την µορφή του. είτε στο σχήµα 7.1 όπου και στα δυο δένδρα έχουν εισαχθεί τα ίδια ακριβώς στοιχεία αλλά µε διαφορετική σειρά. Στο δένδρο T τα στοιχεία έχουν εισαχθεί µε σειρά 1, 2, 3,4,5,6,7 και στο T µε σειρά 4,2, 6,1, 3, 5, 7. Ο λόγος που το δένδρο T είναι καλύτερο από το δένδρο T είναι ότι το T είναι ισοζυγισµένο ενώ το T όχι. Τα ισοζυγισµένα δέντρα επιλύουν το παραπάνω πρόβληµα εκτελώντας µετασχηµατισούς (tree rottions) σε κάθε εισαγωγή - διαγραφή κόµβου µε σκοπό την µείωση του ύψους. Η διαδικασίες ανασυγκρότησης των δέντρων αυτού του τύπου έχουν ϕυσικά υπολογιστικό κόστος, το οποίο αντισταθµίζεται από την µείωση του ύψους που επιταχύνει τις υπόλοιπες λειτουργίες. AVL Serh Trees Τα ισοζυγισµένα δέντρα κατά ύψος (AVL trees) ανήκουν στην κατηγορία των self-lning inry serh trees και είναι η πρώτη δοµή αυτού του τύπου που επινοήθηκε. Το AVL tree ονοµάστηκε έτσι από τα αρχικά των δηµιουργών του, G.M. Adelson-Velsky & E.M. Lndis, που το δηµοσίευασαν το 1962 στην εργασία µε τίτλο An lgorithm for the orgniztion of informtion. Σε ένα AVL tree το ύψος των δυο υποδέντρων οποιουδήποτε κόµβου µπορεί να διαφέρει το πολύ κατά ένα. (Θυµίζουµε ότι ύψος ενός κόµβου, είναι το µήκος του µακρύτερου µονοπατιού από τον κόµβο αυτό µέχρι ένα ϕύλο του δένδρου.) Η αναζήτηση, η εισαγωγή και η διαγραφή απαιτούν χρόνο της 25
Σχήµα 7.1: Εκφυλισµένο (T ) και ισοζυγισµένο (T ) δυαδικό δέντρο τάξης του O(log n) όπου n είναι ο αριθµός των κόµβων πρίν την εκτέλεση της. Ωστόσο η εισαγωγή και η διαγραφή συνήθως απαιτούν µία η περισσότερες περιστροφές (tree rottions) µε σκοπό την µείωση του ύψους του δέντρου. Ο παράγοντας ισορροπίας (lne ftor - f ) ή ισοζύγιο ενός κόµβου, είναι ο αριθµός που προκύπτει αν αφαιρέσουµε από το ύψος του δεξιού υποδέντρου το ύψος του αριστερού. Ενας κόµβος µε lne ftor 1, 0 ή 1 ϑεωρείται ισοζυγισµένος κατά ύψος. Οποιαδήποτε άλλη τιµή χαρακτηρίζει ένα µη ισοζυγισµένο κόµβο, που απαιτεί µετασχηµατισµό του δέντρου. Θυµίζουµε ότι για ένα κόµβο µε αριστερό υποδένδρο Α και δεξιό υποδένδρο Β, το ισοζύγιο είνα Β-Α. Ορισµός 7.1. Ενα δυαδικό δέντρο λέγεται ισοζυγισµένο κατά ύψος ( height lned tree - AVL tree) (σχήµα 7.2) αν για κάθε κόµβο του δέντρου ικανοποιούνται οι εξής δυο συνθήκες : Ο κόµβος είναι ισοζυγισµένος κατά ύψος (f 1,0,1) Τα δύο υποδέντρα του κόµβου είναι επίσης ισοζυγισµένα κατά ύψος Σχήµα 7.2: Παράδειγµα ισοζυγισµένου δέντρου 26
Σχήµα 7.3: Παράδειγµα µη ισοζυγισµένου δέντρου Περιστροφές δέντρου - Tree rottions Μια περιστροφή σε ένα δυαδικό δέντρο αναζήτησης είναι µια διαδικασία η οποία αλλάζει την δοµή του δέντρου χωρίς όµως να επηρεάζει την σειρά των στοιχείων. Χρησιµοποιείται για να αλλάξει την µορφή του δέντρου και συγκεκριµένα µετακινεί µικρότερα υποδέντρα σε χαµηλότερα επίπεδα και µεγαλύτερα υποδέντρα σε υψηλότερα. Υπάρχει µια δυγνωµία στην ϐιβλιογραφία σχετικά µε τον ορισµό και την ονοµατολογία των περιστροφών. Η µια πλευρά υποστηρίζει ότι η κατεύθυνση της περιστροφής άρα και η ονοµατολογία της καθορίζεται από την πλευρά του δέντρου προς την οποία περιστρέφονται οι κόµβοι. Η άλλη πλευρά υποστηρίζει ότι η κατεύθυνση της περιστροφής καθορίζεται από το ποιός κόµβος παίρνει την ϑέση του κόµβου - γονέα. Εµείς ϑα χρησιµοποιήσουµε την πρώτη ερµηνεία (σχήµα 7.4). Μια περιστροφή ονοµάζεται δεξιά περιστροφή όταν οι κόµβοι µετατοπίζονται δεξιόστροφα κατά την ϕορά των δεικτών του ϱολογιού. Η αντίστροφη περιστροφή είναι η αριστερή περιστροφή όπου οι κόµβοι µετατοπίζονται αριστερόστροφα όπως ϕαίνεται στο σχήµα 7.4. Η δεξιά περιστροφή για παράδειγµα µπορεί να περιγραφεί και µε τον ακόλουθο τρόπο: Set P to e the new root. Set Q s left hild to e P s right hild. Set P s right hild to e Q. Οταν ένα δέντρο περιστρέφεται, τότε το υποδέντρο προς το οποίο γίνεται η περιστροφή αυξάνει το ύψος του κατά ένα, ενώ το άλλο υποδέντρο µειώνει το ύψος του κατά ένα. Η λειτουργία αυτή είναι ιδιαίτερα χρήσιµη στα ισοζυγισµένα δέντρα. Εισαγωγή κόµβου σε AVL tree Κατά την είσαγωγή ενός νέου κόµβου, αν το f κάθε κόµβου είναι 1, 0 ή 1 τότε το δέντρο είναι ισοζυγισµένο και δεν απαιτείται καµία περιστροφή. Αν το f σε κάποιο κόµβο πάρει τιµή 2 ή 2 τότε το υποδέντρο µε ϱίζα τον κόµβο αυτό δεν είναι ισοζυγισµένο και απαιτείται περιστροφή του δέντρου. Συνήθως µια ή το πολύ δυο περιστροφές είναι ικανές για να επαναφέρουν το δέντρο σε ισοζυγισµένη µορφή. Υπάρχουν συνολικά τέσσερις περιπτώσεις περιστροφής µε τις δυο από αυτές να είναι συµµετρικές των υπολοίπων. Για την διευκόλυνση της περιγραφής ϑα ονοµάζουµε την ϱίζα του µη ισοζυγισµένου υποδέντρου P, το δεξί παιδί του κόµβου αυτού R και το αριστερό L. 27
Σχήµα 7.4: εξιά και αριστερή στροφή Αν το f του κόµβου P είναι 2, αυτό σηµαίνει ότι το δεξί υποδέντρο έχει µεγαλύτερο ύψος από το αριστερό, οπότε και πρέπει να εξεταστεί το ϐφ του δεξιού παιδιού R. Αν το f του R είναι 1, τότε η εισαγωγή έγινε στην δεξιά πλευρά του R, οπότε απαιτείται µια αριστερή περιστροφή µε ϱίζα τον κόµβο P. Αν το f του R είναι 1 τότε η εισαγωγή έγινε αριστερά του κόµβου R και απαιτείται διπλή περιστροφή. Η πρώτη είναι µια δεξιά περιστροφή µε ϱίζα το R και η δεύτερη είναι µια αριστερή περιστροφή µε ϱίζα το P. Η άλλες δύο περιπτώσεις προκύπτουν όταν το f του κόµβου P είναι 2 και είναι συµµετρικές των παραπάνω. Ο ψευδοκώδικας για τα παραπάνω είναι: IF tree is right hevy (f=2) IF tree s right sutree is left hevy (f=-1) ELSE Perform Left-Right Rottion Perform Single Left rottion ELSE IF tree is left hevy (f=-2) IF tree s left sutree is right hevy (f=1) Perform Right-Left Rottion ELSE Perform Single Right rottion ιαγραφή Αν ο κόµβος, που ϑέλουµε να διαγράψουµε είναι ϕύλλο τότε απλά τον αφαιρούµε από το δέντρο. Στην περίπτωση που δεν είναι ϕύλλο τον αντικαθιστούµε είτε µε τον µεγαλύτερο από το αριστερό του 28
υποδέντρο (inorder predeessor) έιτε µε το µικρότερο από το δεξί του υποδέντρο (inorder suessor). Ο κόµβος αντικατάστασης έχει το πολύ ένα υποδέντρο. Μετά την διαγραφή ακολουθούµε το µονοπάτι από τον κόµβο αντκατάστασης προς την ϱίζα ανανεώνοντας τους lne ftors των κόµβων που συναντάµε. Η διαδικασία αυτή σταµατά αν το f είναι 1 ή 1 οπότε και το ύψος του υποδέντρου έχει µείνει αµετάβλητο. Αν το f είναι 0 τότε το ύψος του δέντρου έχει µειωθεί κατά 1 και η διαδικασία πρέπει να συνεχιστεί. Αν το f είναι 2 ή 2 τότε το υποδέντρο δεν είναι ισοζυγισµένο και πρέπει να περιστραφεί για να επανέλθει σε AVL µορφή. Αν η περιστροφή δώσει f ίσο µε 0 τότε η διαδικασία retrking πρέπει να συνεχιστεί καθώς το ύψος του υποδέντρου έχει µειωθεί κατά 1. Ο υπολογιστικός χρόνος που απαιτείται είναι O(log n) για την αναζήτηση, συν mximum of O(log n) περιστροφές στην διαδικασία re-trking προς την ϱίζα, οπότε ολόκληρη η διαδικασία απαιτεί χρόνο της τάξης του O(log n). Αναζήτηση Η αναζήτηση σε ένα AVL δέντρο είναι πανοµοιότυπη µε την αναζήτηση σε ένα BS δέντρο. Λόγω του ισοζυγισµένου ύψους του δέντρου ο υπολογιστικός χρόνος που απαιτείται είναι της τάξης του O(log n). Παραδείγµατα περιστροφών Left Rottion (LR) Εστω ότι έχουµε την παρακάτω κατάσταση. Για να επαναφέρουµε το δένδρο σε ισοζυγισµένη κατάσταση πραγµατοποιούµε µια αριστερή περιστροφή στο. Αυτό γίνεται µε τα ακόλουθα ϐήµατα: 1. το γίνεται η καινούργια ϱίζα 2. το παίρνει το αριστερό παιδί του ως δεξί του παιδί, ή όπως στην περίπτωση µας, το κενό 3. το παίρνει το σαν αριστερό του παιδί Και προκύπτει το νέο δένδρο. 29
Right Rottion (RR) Είναι το αντίστροφο της αριστερής περιστροφής. Εστω οτι έχουµε την παρακάτω αρχική κατάσταση: Για να το µεταρέψουµε σε ισοζυγισµένο ενεργούµε ανάλογα: 1. το γίνεται η νέα ϱίζα 2. το παίρνει το δεξί παιδί του ως αριστερό παιδί, που στην περίπτωση µας είναι το κενό 3. το παίρνει το σαν δεξί του παιδί Και προκύπτει το νέο δένδρο. Left-Right Rottion (LRR) ή Doule left Κάποιες ϕορές η απλή αριστερή περιστροφή δεν είναι αρκετή. είτε το παράδειγµα: Το δένδρο είναι ισοζυγισµένο. Ας εισάγουµε το. Τώρα πλέον δεν είναι. Ας δοκιµάσουµε να το διορθώσουµε µε απλή αριστερή περιστροφή. 30
Εχουµε καταλήξει στο ίδιο σηµείο. Αν προσπαθήσουµε να δοκιµάσουµε δεξιά περιστροφή σ αυτήν την περίπτωση ϑα καταλήγαµε όπως είχαµε ξεκινήσει. Αυτό είναι αποτέλεσµα του γεγονότος ότι το δεξιό υποδένδρο έχει αρνητικό ισοζύγιο. Επειδή το δεξιό υποδένδρο αφέθηκε να είναι αριστερά ϐαρύ, η περιστροφή µας δεν είχε αποτέλεσµα. Και πως λύνεται Η απάντηση είναι να επιχειρήσουµε µια δεξιά περιστροφή στο δεξιό υποδένδρο. Οχι περιστροφή µε ϐάση την τωρινή ϱίζα αλλά µε ϐάση το δεξί παιδί. Ας αποµονώσουµε το δεξιό υποδένδρο από το υπόλοιπο δένδρο. Εχουµε: και µετά τη δεξιά στροφή γύρω από το : Αφού πραγµατοποιήσουµε περιστροφή στο δεξί µας υποδένδρο, έχουµε προετοιµάσει την ϱίζα µας για αριστερή περιστροφή. Ετσι είναι τώρα το δένδρο µας. 31
Φαίνεται πλέον καθαρά ότι είµαστε έτοιµοι για αριστερή περιστροφή: Right-Left Rottion (RLR) ή Doule right Μια δεξιά-αριστερή περιστροφή είναι η περιστροφή που χρειαζόµαστε όταν επιχειρούµε να ισοζυγίσουµε ένα δένδρο που το αριστερό υποδένδρο είναι δεξιά ϐαρύ. Ας δούµε ένα παράδειγµα πότε την χρειαζό- µαστε: Το δένδρο είναι µη ισοζυγισµένο. Το αριστερό υποδένδρο έχει ύψος 2 και το δεξί υποδένδρο έχει ύψος 0. Άρα το ισοζύγιο της ϱίζας είναι 2. Μια απλή δεξιά περιστροφή δεν ϑα έλυνε το πρόβληµα. Ας τη δοκιµάσουµε: Προφανώς δεν ϐοήθησε. Απλά τώρα έχουµε ένα δένδρο µε ισοζύγιο 2. Ο λόγος που δεν δούλεψε η δεξιά περιστροφή είναι γιατί το αριστερό υποδένδρο, δηλ το, έχει ϑετικό ισοζύγιο και άρα είναι δεξιά ϐαρύ. Η λύση είναι να κάνουµε το αριστερό υποδένδρο µας αριστερά ϐαρύ. Το πετυχαίνουµε εκτελώντας αριστερή περιστροφή στο αριστερό υποδένδρο. Αν το επιχειρήσουµε αυτό καταλήγουµε έτσι: 32
Αυτό όµως το δένδρο µπορούµε να το ισοζυγίσουµε εκτελώντας µια απλή δεξιά περιστροφή. Την επιχειρούµε ξεκινώντας µε ϱίζα το και καταλήγουµε έτσι: Links Animted AVL trees: http://www.s.jhu.edu/ goodrih/ds/trees/vltree.html http://wepges.ull.es/users/jrier/doeni/avl/avl%20tree%20pplet.htm Wikipedi pge for Tree Rottions http://en.wikipedi.org/wiki/tree_rottion Ασκηση 7.1 Κατασκευάστε ένα ισοζυγισµένο δέντρο αναζήτησης µε ϐάση τον κώδικα και την ϕιλοσοφία κατασκευής του δυαδικού δέντρου αναζήτησης που παρουσιάσαµε σε προηγούµενο εργαστήριο. Σηµείωση: Ακολουθώντας µια ottom up προσσέγγιση µπορείτε να ξεκινήσετε κατασκευάζοντας αρχικά τον κόµβο του δέντρου που είναι ουσιαστικά ένα TreeNode µε επιπλέον χαρακτηριστικά. Στην συνέχεια µπορείτε να προχωρήσετε στην κατασκευή του ισοζυγισµένου δέντρου που είναι ένα BSTree µε επιπλέον µεθόδους για τις περιστροφές. Γράφοντας τον κώδικα για τις περιστροφές µπορείτε στην συνέχεια να τις ενσωµατώσετε στις µεθόδους εισαγωγής και διαγραφής. 33