Κατέβασμα παρουσίασης
Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε
1
Διδάσκων: Δρ. Τσίντζα Παναγιώτα
ΔΟΜΕΣ ΔΕΔΟΜΕΝΩΝ Διδάσκων: Δρ. Τσίντζα Παναγιώτα
2
Δέντρο – ορισμός Ένα συνεκτικό, άκυκλο και μη κατευθυνόμενο γράφημα
Για την υλοποίηση: από κάθε κόμβο δεν ξεκινά ένας μόνο δείκτης που να δείχνει μόνο σε ένα κόμβο αλλά περισσότεροι οι οποίοι δείχνουν σε πολλούς κόμβους Οι σχέσεις σύνδεσης των στοιχείων μεταξύ τους είναι σύνθετες προσδιορίζουν κάθε φορά το είδος του δένδρου και τις πράξεις οι οποίες εκτελούνται σ’ αυτό. Είναι από τις πιο αποτελεσματικές δομές για αποθήκευση και επεξεργασία μεγάλου όγκου δεδομένων και χρησιμοποιούνται ευρύτατα σε κάθε είδος εφαρμογής.
3
Δέντρα-Ορισμός (2) Σύνολο κόμβων που συνδέονται με ακμές
για οποιουσδήποτε δύο κόμβους υπάρχει ένα μοναδικό μονοπάτι που τους ενώνει. Δομή ενός πραγματικού δέντρου => κάποια από τα στοιχεία του ακολουθούν την ονοματολογία δένδρου. Ο αρχικός κόμβος από τον οποίο ξεκινούν ακμές αλλά δεν υπάρχει ακμή που να φτάνει σ’ αυτόν ονομάζεται ρίζα (root) του δένδρου. Σε κάθε κόμβο καταλήγει μία ακμή ενώ από αυτόν ξεκινούν περισσότερες που δείχνει η κάθε μία σε έναν κόμβο. Οι κόμβοι στους οποίους καταλήγει μία ακμή αλλά δεν ξεκινά από αυτούς καμία ονομάζονται φύλλα του δένδρου.
4
Όροι (1) Πρόγονοι ενός κόμβου ή φύλλου: είναι οι κόμβοι που βρίσκονται πάνω στο μονοπάτι από τον κόμβο αυτό προς τη ρίζα. Υπόδενδρο: το δένδρο που σχηματίζεται, αν ως ρίζα ληφθεί ένας οποιασδήποτε κόμβος. Απόγονοι ενός κόμβου: οι κόμβοι και τα φύλλα που βρίσκονται στο υπόδενδρο κάτω από αυτόν. Πατέρας ενός κόμβου ή ενός φύλλου: ο κοντινότερος πρόγονος του. Παιδιά ενός κόμβου: οι απόγονοι του για τους οποίους είναι πατέρας. Βαθμός ενός κόμβου: είναι ίσος με το πλήθος των παιδιών του. Βάθος ενός κόμβου ή ενός φύλλου: είναι ίσο με το μήκος (πλήθος πλευρών) του μονοπατιού από αυτόν προς την ρίζα.
5
Όροι (2) Ύψος ενός δένδρου: είναι ίσο με το βάθος του φύλλου με το μεγαλύτερο βάθος. Επίπεδο ενός κόμβου: είναι ο αριθμός των προγόνων του μέχρι τη ρίζα συν 1. Φυλλοπροσανατολιζόμενα δένδρα: τα δένδρα στα οποία τα δεδομένα είναι αποθηκευμένα μόνο στα φύλλα ενώ στους κόμβους αποθηκεύεται μόνο βοηθητική πληροφορία. Κομβοπροσανατολισμένα δένδρα: τα δένδρα στα οποία τα δεδομένα είναι αποθηκευμένα στα φύλλα και στους κόμβους. κ-δικό: το δένδρο όπου κάθε κόμβος έχει βαθμό το πολύ κ.
6
Όροι: παράδειγμα Πρόγονοι του κόμβου D είναι οι κόμβοι B και Α.
Απόγονοι του κόμβου Β είναι οι κόμβοι D και E. Πατέρας του κόμβου D είναι οι κόμβος Β Παιδιά του κόμβου Β είναι οι κόμβοι D και E Ο βαθμός του κόμβου Β είναι 2 Το βάθος του κόμβου D είναι 2 Το ύψος του δένδρου είναι 3 Φυλλοπροσανατολιζόμενο δένδρο Δυαδικό δέντρο
7
Πλήθος κόμβων Ένα δένδρο βαθμού d και ύψους h μπορεί να έχει το πολύ :
κόμβους. Όταν το δένδρο περιέχει το μέγιστο βαθμό κόμβων λέγεται πλήρες. Έτσι ο αριθμός των κόμβων ενός πλήρους δυαδικού δένδρου ύψους h είναι: 2^h-1. Ένα πλήρες δυαδικό δένδρο με n κόμβους έχει ύψος τουλάχιστον logn. Σε κάθε δυαδικό δένδρο με n0 φύλλα και n1 κόμβους (που δεν είναι φύλλα και έχουν βαθμό 2), ισχύει: n0 = n1 +1
8
Κατηγορίες Στατικά δέντρα: δεν επιδέχονται καμιά αλλαγή στην μορφή τους Ημι-δυναμικά: παρέχουν κάποιες λειτουργίες τροποποίησης Δυναμικά: δίνουν τη δυνατότητα μέσω των πράξεων σ’ αυτά να μεταβάλλονται οι κόμβοι του δένδρου.
9
Υλοποίηση δένδρου Αρκετές ομοιότητες με την υλοποίηση λίστας
Κάθε κόμβος του δένδρου περιέχει τόσο τα περιεχόμενα του όσο και ένα σύνολο δεικτών ίσο με τον βαθμό του (το πλήθος των παιδιών του). Διαφορά: Ενώ στη απλή διασυνδεμένη από κάθε κόμβο ξεκινούσε ένας δείκτης προς τον επόμενο, στο δένδρο από κάθε κόμβο ξεκινούν τόσοι δείκτες όσα είναι τα παιδιά του. Στα φύλλα του δένδρου, οι δείκτες τους έχουν την τιμή NULL. Υλοποίηση με: Πίνακα Εγγραφές
10
Υλοποίηση δένδρου με πίνακα
Χρησιμοποιεί έναν δυσδιάστατο πίνακα. Κάθε γραμμή του πίνακα αντιστοιχεί σε ένα κόμβο ή φύλλο. Αν h είναι το ύψος του δένδρου το συνολικό μέγεθος του πίνακα στα πλήρη δυαδικά δένδρα είναι 2^h-1. Σε κάθε μία γραμμή, αποθηκεύεται τόσο το περιεχόμενο του κόμβου όσο οι δείκτες στα παιδιά του. Συνήθως στην πρώτη γραμμή του πίνακα αποθηκεύεται η ρίζα του δένδρου ή αλλιώς χρησιμοποιείται μία μεταβλητή που να δείχνει τη θέση της ρίζας μέσα στον πίνακα.
11
Παράδειγμα πίνακα Οι κόμβοι αριθμούνται από αριστερά προς τα δεξιά και από επίπεδο 1 της ρίζας μέχρι το τελευταίο επίπεδο των φύλλων του δένδρου. Σε ένα πλήρες δυαδικό δένδρο ύψους h, με πλήθος κόμβων 2^h-1, για κάθε κόμβο i ισχύουν: 1. Ο πατέρας του βρίσκεται στη θέση [i/2] του πίνακα. 2. Το αριστερό παιδί του κόμβου i βρίσκεται στη θέση 2i, αν 2i<=n. Αλλιώς ο κόμβος i δεν έχει αριστερό παιδί. 3. Το δεξί παιδί του κόμβου i βρίσκεται στη θέση 2i+1, αν 2i+1<=n. Αλλιώς ο κόμβος i δεν έχει δεξί παιδί. ΘΕΣΗ ΑΡΙΣΤΕΡΟ ΠΑΙΔΙ ΠΕΡΙΕΧΟΜΕΝΟ ΚΟΜΒΟΥ ΔΕΞΙ ΠΑΙΔΙ 1 2 A 3 4 B 5 6 C 7 D E F G
12
Υλοποίηση με εγγραφές Κάθε κόμβος ή φύλλο ενός κ-δικού δένδρου αποθηκεύεται σαν μία εγγραφή (ειδική δομή) η οποία περιέχει τόσο το περιεχόμενο του κόμβου όσο και k δείκτες ως μεταβλητές (ή έναν πίνακα δεικτών k θέσεων) για την υπόδειξη των k παιδιών του. Χρησιμοποιείται μία μεταβλητή τύπου δείκτη που υποδεικνύει την εγγραφή της ρίζας. Πλεονέκτημα: η δέσμευση της απαραίτητης μνήμης γίνεται δυναμικά και ανάλογα με τις τρέχουσες απαιτήσεις. Στην περίπτωση του πίνακα είναι δυνατό να γεμίσει αυτός και να μην “χωράει” άλλα στοιχεία ή και να έχει λίγα στοιχεία ενώ έχει δεσμευτεί το σύνολο του αποθηκευτικού χώρου (σπατάλη μνήμης).
13
Υλοποίηση με εγγραφές Υλοποίηση με δείκτες: Η πιο συνηθισμένη υλοποίηση. Κάθε στοιχείο αντιστοιχεί σε ένα κόμβο ο οποίος περιλαμβάνει ένα πεδίο δεδομένων (data) και δύο πεδία συνδέσμων (LeftChild, RightChild)
14
Βασικές λειτουργίες δένδρων
element(k): επιστρέφει το στοιχείο του κόμβου k. father(k): επιστρέφει το δείκτη προς τον πατέρα του κόμβου k. children(k): επιστρέφει το δείκτη προς τα παιδιά του κόμβου k. Συνήθεις πράξεις BT GetHeight(): Προσδιορισμός ύψους δένδρου GetNumberElements(): Προσδιορισμός στοιχείων δένδρου. CopyTree(): Αντιγραφή δένδρου. DeleteTree(): Διαγραφή δένδρου FindifTree(): Έλεγχος ΔΔ αν είναι BT. OutputTree(): Παρουσίαση δένδρου (οθόνη ή printer). Όλα τα παραπάνω εκτελούνται με συστηματικό τρόπο με τη λειτουργία διάσχισης του δένδρου. Κάθε διαδικασία επίσκεψης όλων των κόμβων ενός δένδρου, ακριβώς μια φορά τον καθένα, ονομάζεται διάσχιση (traversal).
15
Διάσχιση δένδρων - Μέθοδοι
Επίσκεψη όλων των κόμβων του δένδρου ακριβώς μία φορά. 3 μέθοδοι διέλευσης δένδρων: η προδιάταξη, η μεταδιάταξη και η συμμετρική διάταξη. Κάθε μία από αυτές τις μεθόδους ορίζεται ανάλογα με την σειρά εκτέλεσης των παρακάτω τριών λειτουργιών: Επίσκεψη στη ρίζα. Επίσκεψη στο αριστερό υπόδενδρο. Επίσκεψη στο δεξιό υπόδενδρο. Ο χρόνος διάσχισης των δένδρων είναι γραμμικός: Θ(n)
16
Προδιάταξη (preorder)
Αλγόριθμος Preorder (Start) Δεδομένα //Start: Δείκτης ρίζας// Αρχή Όσο Start <> NULL εκτέλεσε Εκτύπωσε Start.info //Επίσκεψη ρίζας L=Start.Left //Επίσκεψη αριστερού παιδιού Preorder(L) R=Start.Right //Επίσκεψη δεξιού παιδιού Preorder(R) Τέλος Όσο Τέλος Αποτελέσματα //κόμβοι// Τέλος Preorder Αποτέλεσμα επίσκεψης preorder A, B, D, E, C, F και G Προσοχή: η σειρά επίσκεψης είναι Επίσκεψη στη ρίζα. Επίσκεψη στο αριστερό υπόδενδρο. Επίσκεψη στο δεξιό υπόδενδρο.
17
Μεταδιάταξη (Postorder)
Αλγόριθμος Postorder (Start) Δεδομένα //Start: Δείκτης ρίζας// Αρχή Όσο Start <> NULL εκτέλεσε L=Start.Left //Επίσκεψη αριστερού παιδιού Preorder(L) R=Start.Right //Επίσκεψη δεξιού παιδιού Preorder(R) Επίσκεψη ρίζας Εκτύπωσε Start.info Τέλος Όσο Τέλος Αποτελέσματα //κόμβοι// Τέλος Postorder Αποτέλεσμα επίσκεψης postorder D, E, Β, F, G, C και A Προσοχή η σειρά επίσκεψης είναι: Επίσκεψη στο αριστερό υπόδενδρο. Επίσκεψη στο δεξιό υπόδενδρο. Επίσκεψη στη ρίζα
18
Συμμετρική διάταξη (Symmetric order ή inorder)
Αλγόριθμος Inorder (Start) Δεδομένα //Start: Δείκτης ρίζας// Αρχή Όσο Start <> NULL εκτέλεσε L=Start.Left //Επίσκεψη αριστερού παιδιού Preorder(L) Εκτύπωσε Start.info //Επίσκεψη ρίζας R=Start.Right //Επίσκεψη δεξιού παιδιού Preorder(R) Τέλος Όσο Τέλος Αποτελέσματα //κόμβοι// Τέλος Preorder Αποτέλεσμα επίσκεψης postorder D, Β, E, A, F, C και G Προσοχή η σειρά επίσκεψης είναι: Επίσκεψη στο αριστερό υπόδενδρο. Επίσκεψη στη ρίζα Επίσκεψη στο δεξιό υπόδενδρο.
19
Άσκηση Αριθμήστε τους κόμβους του παρακάτω δένδρου σύμφωνα με τη σειρά επίσκεψης του από τους 3 αλγόριθμους διάσχισης InOrder PostOrder PreOrder
20
Μετατροπή παραστάσεων στον πολωνικό συμβολισμό
Έστω η παράσταση : Α*Β+C*D.
21
Δυαδικά δένδρα αναζήτησης
Ένα ΔΔΑ (Binary Search Tree, BST) είναι δυαδικό δένδρο με διακριτά κλειδιά (τιμές) και τις εξής ιδιότητες: Τα κλειδιά (αν υπάρχουν) στο αριστερό υποδένδρο της ρίζας είναι μικρότερα από το κλειδί της ρίζας. Τα κλειδιά (αν υπάρχουν) στο δεξιό υποδένδρο της ρίζας είναι μεγαλύτερα από το κλειδί της ρίζας. Το αριστερό και το δεξιό υποδένδρο είναι επίσης ΔΔΑ. Στόχος: να μειώσουμε τους χρόνους ενημέρωσης και αναζήτησης σε λιγότερο από Θ(n).
22
Ύψος ΔΔΑ Το ύψος ενός ΔΔΑ με n στοιχεία μπορεί να φτάσει μέχρι και n.
Στη γενική περίπτωση όμως (όταν οι εισαγωγές και οι διαγραφές γίνονται τυχαία),το υψος είναι O(logn).
23
Βασικές πράξεις σε ΔΔΑ Create(): δημιουργία ενός κενού ΔΔΑ
Search (k, e): επιστρέφει στο e το στοιχείο με κλειδί k επιστρέφει false αν η πράξη αποτύχει (δεν βρέθηκε το στοιχείο Επιστρέφει true αν το στοιχείο βρεθεί Insert(e): εισάγει το στοιχείο e στο ΔΔΑ Delete(k, e): διαγράφει το στοιχείο με κλειδί k και επιστρέφει την τιμή στο e Ascend(): δίνει στο Output όλα τα στοιχεία του δέντρου σε φθίνουσα διάταξη
24
Υλοποίηση κόμβου ενός ΔΔΑ
Κάθε ένας κόμβος του δυαδικού δένδρου αποτελεί μία συγκεκριμένη διακριτή οντότητα. Θα πρέπει να οριστεί κάθε κόμβος σαν μία μεταβλητή δομής η οποία περιέχει τόσο το περιεχόμενο – στοιχείο όσο και δύο δείκτες προς τα δύο του παιδιά. Το περιεχόμενο μπορεί να είναι μία απλή μεταβλητή π.χ. ακεραίων. Τότε στη γλώσσα C, κάθε κόμβος θα έχει την μορφή: Struct bintree { int num; Struct bintree *left; Struct bintree *right; } node; Παράλληλα, οι δείκτες που λειτουργούν σαν σύνδεσμοι του κόμβου με τα παιδιά του έχουν την μορφή Struct bintree *left και Struct bintree *right; Ο συμβολισμός * σημαίνει ότι χρησιμοποιείται ένας δείκτης διευθύνσεων ως σύνδεσμος. Αυτός ο δείκτης σε κάθε κόμβο περιέχει την διεύθυνση μνήμης που περιέχει τον επόμενο κόμβο (αριστερό ή δεξί παιδί). Τέλος, πρέπει να τονιστεί ότι σε κάθε περίπτωση, για την επεξεργασία ενός δένδρου υπάρχει ένας δείκτης, έστω root, ο οποίος δείχνει στο πρώτο στοιχείο του δένδρου που είναι η ρίζα του. Δυαδικό δένδρο με ακέραια κλειδιά: με τον συμβολισμό node.num ορίζουμε τον ίδιο τον ακέραιο αριθμό του κόμβου ενώ με τον συμβολισμό node.left ορίζουμε τον δείκτη του κόμβου προς τον επόμενο αριστερό κόμβο-παιδί ενώ node.right ορίζουμε τον δείκτη του κόμβου προς τον επόμενο δεξιό κόμβο-παιδί.
25
Αναζήτηση στοιχείου Αλγόριθμος Search
Δεδομένα // Ο δείκτης root που δείχνει στη τρέχουσα ρίζα του δένδρου. x: ο ζητούμενος αριθμός ή κόμβος Flag: μία λογική μεταβλητή Αρχή Current_node=root; Όσο (Current_node!=0) και (flag = FALSE) εκτέλεσε //όσο δεν έχω ελέγξει όλο το δέντρο και δεν έχω βρει ακόμα το στοιχείο Αν (x = Current_node.num) τότε flag = TRUE αλλιώς Αν (x < Current_node.num) τότε Current_node= Current_node.left Current_node= Current_node.right Τέλος Αν Τέλος Όσο Τέλος Αποτελέσματα // Αν flag = FALSE ανεπιτυχής αναζήτηση αλλιώς επιτυχής// Τέλος Search Μέσω του δείκτη root που δείχνει στην ρίζα του δένδρου προσπελαύνουμε το πρώτο του στοιχείο δηλαδή την ρίζα. Αν ο αριθμός που αναζητούμε είναι μικρότερος από την τιμή της ρίζας του δένδρου (node.num) τότε συνεχίζουμε με το αριστερό υπόδενδρο. Δηλαδή ακολουθώντας τον δείκτη που δείχνει στο αριστερό υπόδενδρο (node.left) ακολουθούμε την ίδια διαδικασία με το αριστερό παιδί του κόμβου. Αντίστοιχα, αν ο αριθμός που αναζητούμε είναι μεγαλύτερος από την τιμή της ρίζας του δένδρου (node.num) τότε συνεχίζουμε με το δεξιό υπόδενδρο μέσω του δείκτη (node.right). Η αναζήτηση σταματά όταν βρεθεί ο αριθμός ως τιμή σε κάποιον κόμβο του δένδρου οπότε και επιστρέφεται ο κόμβος αυτός ή φτάνουμε σε κενό δείκτη, δηλαδή σε φύλλο γεγονός που σημαίνει ότι ο αριθμός δεν περιέχεται σε κανέναν κόμβο του δένδρου.
26
Εισαγωγή στοιχείου Βασική ιδιότητα ΔΔΑ: Διαδικασία:
Η εισαγωγή γίνεται πάντα σε κάποιο (νέο) φύλλο Διαδικασία: Αναζήτηση του στοιχείου (οπότε καταλήγουμε σε κόμβο- φύλλο). Εισαγωγή του ως παιδί εκείνου του κόμβου Κόστος= Κόστος αναζήτησης + κόστος ‘συγκόλλησης’ νέου κόμβου στον πατέρακόμβο = O(h) + O(1) = O(h)
27
Εισαγωγή στοιχείου (1) Η πράξη της εισαγωγής ενός στοιχείου (στη συγκεκριμένο παράδειγμα ακεραίου αριθμού) στο δένδρο γίνεται χρησιμοποιώντας μια αναδρομική συνάρτηση που έχει την ακόλουθη μορφή : struct bintree *BuildTree(struct bintree *c_root, struct bintree *lr_child, int x) Η συνάρτηση καλείται δίνοντας 3 ορίσματα: struct bintree *c_root. Είναι ο κόμβος (διεύθυνση μνήμης της δομής του κόμβου) του οποίου είτε το αριστερό είτε στο δεξί υπόδενδρο θα αποθηκευτεί ο προς εισαγωγή αριθμός x. struct bintree *lr_child. Είναι το αριστερό ή το δεξί παιδί, έστω v (διεύθυνση μνήμης της δομής του κόμβου) του προηγούμνου κόμβου u. Στο υπόδενδρο με ρίζα τον κόμβο v θα αποθηκευτεί ο προς εισαγωγή αριθμός x. int x. Είναι ο προς εισαγωγή αριθμός x.
28
Εισαγωγή στοιχείου (2) Η συνάρτηση BuildTree επιστρέφει ως όρισμα ένας δείκτη διεύθυνσης μνήμης σε κόμβο του δένδρου. Αρχικά το δένδρο είναι άδειο και root=NULL. Τότε καλείται η BuildTree με ορίσματα: struct bintree *BuildTree(root, root, x) Σε κάθε περίπτωση όμως θα καλείται η BuildTree η οποία θα εξετάζει αν ο κόμβος v είναι NULL ή όχι. Στην πρώτη περίπτωση θα δεσμευτεί χώρος στη μνήμη (έστω δείκτης r) για τη δημιουργία ενός νέου κόμβου στον οποίο θα αποθηκευτεί ο προς εισαγωγή αριθμός x (r.num) ενώ οι δείκτες r.left και r.right θα γίνουν NULL. Αν ο κόμβος αυτός είναι η ρίζα του δένδρου τότε επιστρέφει στην root την r. Σε οποιαδήποτε άλλη περίπτωση θα πρέπει να ενημερώσει τον αριστερό ή τον δεξιό δείκτη του πατέρα u του v (πρώτο όρισμα της συνάρτησης) ώστε να δείχνει σωστά στον νέο κόμβο r. Έτσι αν x< c_root.num τότε c_root.left = x αλλιώς c_root.right = x. Στην δεύτερη περίπτωση στην οποία ο κόμβος v περιέχει στοιχείο τότε η διαδικασία θα προχωρήσει είτε προς το αριστερό υπόδενδρο του r αν x<r.num (BuildTree(r, r.left, x)) ή αντίθετα προς το δεξιό υπόδενδρο (BuildTree(r, r.right, x)). Παρατηρούμε έτσι ότι η συνάρτηση BuildTree είναι αναδρομική.
29
Εισαγωγή στοιχείου (3) Αν (x< c_root.left) τότε
c_root.left = lr_child αλλιώς c_root.right = lr_child τέλος Αν Αν (x<lr_child.left) τότε BuildTree(lr_child, lr_child.left, x) BuildTree(lr_child, lr_child.right, x) Τέλος Αν Τέλος Αποτελέσματα //Ο κόμβος στον οποίο εισήχθει ο αριθμός x// Τέλος BuildTree Αλγόριθμος BuildTree Δεδομένα // Ο δείκτης c_root που δείχνει στη ρίζα του τρέχον δένδρου. Ο δείκτης lr_child που δείχνει στο αριστερό ή δεξιό υπόδενδρο της ρίζας c_root . x: ο αριθμός που επιθυμούμε να εισάγουμε Αρχή Αν (lr_child = NULL) τότε // έχουμε φτάσει σε φύλλο άρα κάνουμε εισαγωγή Δημιούργησε νέο κόμβο lr_child lr_child.num=x lr_child.left=NULL lr_child.right=NULL Αν (root=NULL) //τότε το στοιχείο προστίθεται στην ρίζα root=lr_child τέλος Αν
30
Μειονέκτημα εισαγωγής και λύση
Η εισαγωγή τυχαίων αριθμών μέσα σε ένα δένδρο αναζήτησης έχει ως αποτέλεσμα το δένδρο όχι μόνο να μην είναι ισοζυγισμένο αλλά σε πολλές περιπτώσεις (εισαγωγή ταξινομημένων αριθμών) να πλησιάζει η μορφή στην γραμμική λίστα. Θα μπορούσε επομένως να γίνει μία αρχική εισαγωγή στοιχείων και δημιουργία του δένδρου με τέτοιο τρόπο ώστε να είναι αυτό ισοζυγισμένο. Για να γίνει αυτό μπορούμε να ακολουθήσουμε την παρακάτω διαδικασία: Αρχικά ο χρήστης εισάγει όλους τους αριθμούς που θέλει να υποθηκεύσει στο δένδρο, έστω N. Τους αριθμούς αυτούς τους αποθηκεύει αρχικά σε ένα πίνακα. Ταξινομεί τους αριθμούς του πίνακα, έστω από τον μικρότερο προς τον μεγαλύτερο. Καλεί την συνάρτηση εισαγωγής BuildTree δίνοντας της τα στοιχεία από τον πίνακα με τέτοιον τρόπο ώστε αυτά να εισάγονται κάθε φορά σε ένα ισοζυγισμένο δυαδικό δένδρο αναζήτησης. Η επιλογή των αριθμών από τον πίνακα γίνεται ως εξής: Με τη χρήση ενός βρόγχου εισάγονται οι αριθμοί ανά επίπεδο δηλαδή από το επίπεδο 1 της ρίζας έως και το επίπεδο LogN των φύλλων. Στο πρώτο επίπεδο αν ο πίνακας έχει N στοιχεία εισάγεται στο στοιχείο Ν/2 που είναι το μεσαίο του διαστήματος 0 έως Ν του πίνακα. Στο δεύτερο επίπεδο εισάγονται τα μεσαία στοιχεία των διαστημάτων 0 έως Ν/2 και Ν/2 έως Ν κ.ο.κ. Σε κάθε επίπεδο λοιπόν εισάγονται 2h-1 στοιχεία από αντίστοιχα 2h-1 διαστήματα, όπου h το επίπεδο. Μπορούμε να βρίσκουμε τα όρια των διαστημάτων ξεκινώντας κάθε φορά στο επίπεδο h από τη θέση 0 του πίνακα προσθέτοντας την τιμή Ν/2h-1 μέχρι το τέλος του πίνακα (θέση N). Πρέπει να προσέξουμε όταν το δένδρο δεν είναι πλήρες. Για να εισάγει το μεσαίο αριθμό ενός διαστήματος πρέπει να όρια να διαφέρουν τουλάχιστον κατά 2 θέσεις. Επίσης όταν δεν είναι πλήρες το δένδρο, στο τελευταίο επίπεδο πρέπει να εισαχθεί ο αριθμός που είναι αποθηκεμένος στην πρώτη θέση του πίνακα.
31
Διαγραφή στοιχείου σε ΔΔΑ
Διαφορετικές περιπτώσεις: Ο κόμβος (που περιέχει το στοιχείο) είναι φύλλο Ο κόμβος έχει μόνο ένα μη κενό υποδένδρο Ο κόμβος έχει ακριβώς δύο μη κενά υποδένδρα
32
Διαγραφή στοιχείου σε ΔΔΑ
Μέσω του δείκτη root που δείχνει στην ρίζα του δένδρου προσπελαύνουμε το πρώτο του στοιχείο δηλαδή την ρίζα. Αν ο αριθμός που αναζητούμε είναι μικρότερος από την τιμή της ρίζας του δένδρου (node.num) τότε συνεχίζουμε με το αριστερό υπόδενδρο. Δηλαδή ακολουθώντας τον δείκτη που δείχνει στο αριστερό υπόδενδρο (node.left) ακολουθούμε την ίδια διαδικασία με το αριστερό παιδί του κόμβου. Αντίστοιχα, αν ο αριθμός που αναζητούμε είναι μεγαλύτερος από την τιμή της ρίζας του δένδρου (node.num) τότε συνεχίζουμε με το δεξιό υπόδενδρο μέσω του δείκτη (node.right). Αν βρεθεί ο αριθμός ως τιμή σε κάποιον κόμβο του δένδρου τότε ενημερώνει η πράξη ότι ο αριθμός που καλούμαστε να σβήσουμε βρέθηκε στο δένδρο. Αν ο κόμβος διαγραφής είναι φύλλο, γεγονός που σημαίνει ότι και οι δύο δείκτες του (node.left και node.right) για το αριστερό και δεξί παιδί είναι NULL, τότε αρκεί σβήσουμε το φύλλο πηγαίνοντας στον πατέρα του και κάνοντας NULL τον δείκτη (αριστερό ή δεξί) που δείχνει στο φύλλο. Αν ο κόμβος διαγραφής είναι ενδιάμεσος κόμβος τότε εξετάζεται αν έχει δύο παιδιά ή ένα (αν ένας δείκτης node.left ή node.right είναι NULL). Αν έχει ένα παιδί τότε ο κόμβος σβήνεται και αντικαθίσταται από το παιδί του. Αν όμως ο κόμβος που θέλουμε να διαγράψουμε έχει δύο παιδιά (οι δείκτες node.left και node.right δεν είναι NULL) τότε ο κόμβος σβήνεται και αντικαθίσταται είτε από τον αριστερό κόμβο (παιδί του) είτε από τον δεξί. Στην περίπτωση όμως αυτή πρέπει να εκτελεστεί μία συνάρτηση η οποία να υπολογίζει τον κόμβο που θα αντικαταστήσει αυτόν που θα σβήσουμε και να κάνει όλες τις απαραίτητες διορθωτικές ενέργειες που απαιτούνται.
33
Διαγραφή στοιχείου σε ΔΔΑ
αλλιώς // δεν έχει αριστερό παιδί αλλά έχει δεξί Σβήσε κόμβο Current_node Αντικατέστησε τον κόμβο αλλιώς // το στοιχείο δεν βρέθηκε Αν (x < Current_node.num) τότε Current_node= Current_node.left Deletion(Current_node.left,x) // αναδρομή στο αριστερό υποδένδρο αλλιώς Current_node= Current_node.right Deletion(Current_node. right,x) αναδρομή στο δεξί υποδένδρο Τέλος Αν Τέλος Όσο Τέλος Αποτελέσματα //Αν flag = FALSE δεν υπάρχει ο αριθμός αλλιώς επιτυχής διαγραφή// Τέλος deletion Αλγόριθμος deletion Δεδομένα // Ο δείκτης root που δείχνει στη ρίζα του δένδρου x: ο αριθμός που επιθυμούμε να σβήσουμε (ή κόμβος). Flag: μία λογική μεταβλητή. Current_node : μεταβλητή που κρατά τον τρέχον κόμβο εξέτασης Αρχή Current_node=root Flag= FALSE Όσο (Current_node!=0) και (flag = FALSE) εκτέλεσε Αν (x = Current_node.num) τότε // το στοιχείο βρέθηκε Αν (Current_node.left=NULL) και (Current_node.right=NULL) τότε //το στοιχείο είναι φύλλο Σβήσε κόμβο Current_node Ενημέρωσε το δείκτη του πατέρα του να είναι NULL flag= TRUE αλλιώς Αν (Current_node. right =NULL) // δεν έχει δεξί παιδί αλλά έχει αριστερό Ενημέρωσε δείκτη του πατέρα του να δείχνει στο Current_node. left
34
Δένδρα AVL Ένα δυαδικό δένδρο καλείται δένδρο AVL αν και μόνο αν τα ύψη των δύο αριστερού και δεξιού) κάθε εσωτερικού κόμβου διαφέρουν το πολύ κατά 1 |hL- hR| ≤ 1 Κάθε πράξη (διάσχιση, ένθεση, διαγραφή, … ) εκτελείται σε χρόνο (μέση περίπτωση). O(logn)
35
Δένδρα AVL Λειτουργία Η αναζήτηση στοιχείου-κόμβου είναι παρόμοια με την αναζήτηση σε ένα απλό δυαδικό δένδρο αναζήτησης. Μετά από κάθε εισαγωγή και διαγραφή πρέπει να γίνει έλεγχος για το αν ικανοποιείται ο περιορισμός του AVL (διαφορά των υψών των υποδένδρων). Σε περίπτωση που δεν υπάρχει πρόβλημα στη δομή του δένδρου, τότε δεν απαιτείται καμία άλλη ενέργεια Διαφορετικά θα πρέπει να γίνουν δομικές αλλαγές στο δένδρο ώστε να προκύψει πάλι ένα δένδρο Δομικές αλλαγές περιστροφές: Έχουμε δύο είδη περιστροφών Απλή περιστροφή: Περιστρέφονται δύο κόμβοι. Διπλή περιστροφή: Περιστρέφονται τρεις κόμβοι
36
Απλή περιστροφή Περιστροφή Δεξιά Περιστροφή Αριστερά
37
Παράδειγμα εισαγωγής (1)
Αρχικό δένδρο AVL στο οποίο θέλουμε να προσθέσουμε κόμβο με τιμή 2
38
Παράδειγμα εισαγωγής (2)
Δένδρο μετά την εισαγωγή
39
Παράδειγμα εισαγωγής (3)
40
AVL – διπλή περιστροφή
41
Παράδειγμα διαγραφής (1)
Διαγραφή κόμβου 94
42
Παράδειγμα διαγραφής (2)
Δένδρο μετά την διαγραφή
43
Παράδειγμα διαγραφής (3)
Παρόμοιες παρουσιάσεις
© 2024 SlidePlayer.gr Inc.
All rights reserved.