υαδικά δέντρα αναζήτησης οµές εδοµένων 3 ο εξάµηνο Ορισµός δυαδικού δέντρου αναζήτησης Σ ένα δυαδικό δέντρο αναζήτησης, για κάθε κόµβο Χ, Όλα τα κλειδιά(αντικείµενα) στο αριστερό υποδέντρο του Χ έχουν τιµές µικρότερες από το κλειδί του Χ Όλα τα κλειδιά(αντικείµενα) στο δεξί υποδέντρο του Χ έχουν τιµές µεγαλύτερες από το κλειδί του Χ Για τον ορισµό ενός δυαδικού δέντρου αναζήτησης απαιτείται µία σχέση διάταξης προκειµένου να µπορούµε να ορίσουµε τις έννοιες µικρότερο, µεγαλύτερο 1
υαδικόδέντροαναζήτησης υαδικόδέντροόχιόµως δέντρο αναζήτησης Αναζήτηση Η λειτουργία της αναζήτησης εκτελείται επαναληπτικά ακολουθώντας είτε το αριστερό είτε το δεξί κλαδί ανάλογα µε τα αποτελέσµατα της σύγκρισης 2
υαδικάδέντρααναζήτησης: Βήµατα διαδικασίας αναζήτησης Ξεκινάµε την αναζήτηση από τη ρίζα του δέντρου Αν το ζητούµενο αντικείµενο είναι ίσο µετοαντικείµενο στη ρίζα, η διαδικασία αναζήτησης τερµατίζει επιτυχώς Αν το ζητούµενο αντικείµενο είναι µικρότερο από το αντικείµενο της ρίζας, τότε η αναζήτηση συνεχίζεται στο αριστερό υποδέντρο ιαφορετικα (ζητούµενο αντικείµενο είναι µεγαλύτερο από το αντικείµενο της ρίζας, Η αναζήτηση συνεχίζεται στο δεξί υποδέντρο Αν η αναζήτηση καταλήξει σε κάποιον κόµβο απ οπου δεν υπάρχει σύνδεσµος (ακµή) για να συνεχίσει η αναζήτηση τότε το αντικείµενο δεν υπάρχει και η διαδικασία εύρεσής του είναι ανεπιτυχής 3>2 3<7 Αναζήτηση του στοιχείου 3 3<5 6>2 3==3 Αναζήτηση στοιχείου 3 ΕΠΙΤΥΧΗΣ!! 6<7 Αναζήτηση του στοιχείου 6 6>5 ΤΕΛΟΣ!! ΑΝΑΖΗΤΗΣΗ στοιχείου 6 ΑΝΕΠΙΤΥΧΗΣ 3
Αναζήτηση κόµβου σε δυαδικό δέντρο διάσχισης class BinaryNode public int idata; public Node leftchild; public Node rightchild; // data item (key) // this node's left child // this node's right child public BinaryNode find(int key) // find node with given key BinaryNode current = root; // start at root while(current.idata!= key) // while no match, if (key < current.idata) // go left? current = current.leftchild; // or go right? current = current.rightchild; if(current == null) // if no child, return null; // didn't find it return current; // found it // end find() Εισαγωγή κόµβου Ξεκινώντας από την ρίζα του δέντρου αναζητούµε ένακόµβο που περιέχει το αντικείµενο που πρόκειται να εισαχθεί Εάν το αντικείµενο βρεθεί, τότε περιέχεται ήδη στο δέντρο οπότε η διαδικασία της αναζήτησης τερµατίζεται Αν η αναζήτηση είναι ανεπιτυχής φτάσαµε σεένακόµβο από οπου η αναζήτηση δεν µπορεί να συνεχίσει (δεν υπάρχει σύνδεσµος προς παιδί) τότε προσθέτουµε τονέοκόµβο ως παιδί του τρέχοντος κόµβου 4
Εισαγωγή κόµβου root current current current current.rightchild newnode public void insert(int id) // εισαγωγή κόµβου µε κλειδίid Node newnode = new Node(); // δηµιούργησε έναν νέο κόµβο newnode.idata = id; // εισαγωγή id if(root==null) // δεν υπάρχει κόµβος στη ρίζα root = newnode; // µη κενόδέντρο Node current = root; Node ; Παράδειγµα υλοποίησης εισαγωγής while(true) // (όσο δεν βρέθηκε η θέση που θα εισαχθεί ο νέος κόµβος) = current; if(id < current.idata) // ακολουθούµε αριστερόµονοπάτι? current = current.leftchild; if(current == null) // τέλος µονοπατιού // εισαγωγή ως αριστερό παιδί.leftchild = newnode; return; // end if αριστερό µονοπάτι? // ήακολουθούµε δεξίµονοπάτι? current = current.rightchild; if(current == null) // // εισαγωγή ως αριστερό παιδί.rightchild = newnode; return; // end δεξί µονοπάτι? // end while // end // end insert() 5
ιαγραφή κόµβου Ο κόµβος που θα διαγραφεί είναι φύλλο Ο κόµβος που θα διαγραφεί έχει ένα παιδί Ο κόµβος που θα διαγραφεί έχει δύο παιδιά Περίπτωση 1: Ο κόµβος που θα διαγραφεί είναι φύλλο Αλλάζουµε το κατάλληλο πεδίο παιδιού στον πατέρα του κόµβου ώστε να είναι null και να µην δείχνει στον κόµβο if current node is left child.leftchild = null.rightchild =null current node 6
Περίπτωση 2: Ο κόµβος που θα διαγραφεί έχει ένα παιδί Οκόµβος έχει δύο συνδέσεις: Με τον πατέρα του Με το µοναδικό παιδί του Συνδέουµε τον πατέρα του κόµβου που διαγράφουµε απευθείαςµε τοπαιδίτου ιαγραφή κόµβου µε έναπαιδί current ιαγραφή του 5.rightChild =current.leftchild 7
ιαγραφή κόµβου µε ένα υποδέντρο current ypodentro = current.leftchild If ypodentro is null then ypodentro = current.rightchild If.leftChild ==x then.leftchild = ypodentro.rightchild = ypodentro Περίπτωση 3: Ο κόµβος που θα διαγραφεί έχει δύο παιδιά Γιαναδιαγράψουµεένανκόµβο µε δύο παιδιά αντικαθιστούµε τονκόµβο µε τον ενδοδιατεταγµένο διάδοχό του Ενδοδιατεταγµένος διάδοχος ή διάδοχος ενός κόµβου Ο κόµβος µε το επόµενο µεγαλύτερο κλειδί 8
Εύρεση διαδόχου Ξεκινάµε από το δεξί παιδί του κόµβου και ακολουθούµε το µονοπάτι των αριστερών παιδιών ιαγραφή 2 εν υπάρχει αριστερό παιδί Ο 3 είναι διάδοχος του 2 Αντικατάσταση κόµβου 2 µε το διάδοχό του Ο διάδοχος δεξί παιδί του προς διαγραφή κόµβου Μετακίνηση του υποδέντρου που είναι η ρίζα του διαδόχου και τοποθέτησή του εκεί που ήταν ο διαγραµµένος κόµβος 50 πατέρας 50 Βήµα 1 62 75 Πατέρας διαδόχου (current) 87 ιαγραφή 75 ιάδοχος(successor) Βήµα 2 62 87 93 93 1..rightChild = successor; 2. successor.leftchild = current.leftchild; 9
Ο διάδοχος αριστερός απόγονος του δεξιού παιδιού του προς διαγραφή κόµβου Μετακίνηση του υποδέντρου που είναι η ρίζα του διαδόχου και τοποθέτησή του εκεί που ήταν ο διαγραµµένος κόµβος 50 πατέρας 50 Βήµα 3 62 75 (delnode-current) 87 Πατέρας διαδόχου ιαγραφή 75 Βήµα 4 62 77 Βήµα 1 Βήµα 2 87 79 93 ιάδοχος (successor) 77 79 93 1. successorparent.leftchild = successor.rightchild; 2. successor.rightchild = delnode.rightchild; 3..rightChild = successor; 4. successor.leftchild = current.leftchild; Υλοποίηση διαδικασίας εύρεσης διαδόχου // returns node with next-highest value after delnode // goes to right child, then right child's left descendents private Node getsuccessor(node delnode) Node successorparent = delnode; Node successor = delnode; Node current = delnode.rightchild; // go to right child while(current!= null) // until no more // left children, successorparent = successor; successor = current; current = current.leftchild; // go to left child // if successor not if(successor!= delnode.rightchild) // right child, // make connections successorparent.leftchild = successor.rightchild; successor.rightchild = delnode.rightchild; return successor; 10
ιαγραφή κόµβου µε δύο παιδιά ΒΗΜΑ 1 Εάν ο κόµβοςπροςδιαγραφή,current, είναι ρίζα τότε δεν έχει γονέα και απλά περνάµε τηρίζαστοδιάδοχο Αλλιώς Οκόµβοςπου θα διαγαφεί είναι αριστερό ή δεξί παιδί ορίζουµε το κατάλληλο πεδίο (leftchild ή rightchild) στο πατέρα του να δείχνει στο διάδοχο(successor) ΒΗΜΑ 2 Ορίζουµε τοαριστερόπαιδίτουsuccessor ναδείχνειτοαριστερόπαιδίτουcurrent βήµα2 βήµα1 Προσχέδιο υλοποίησης διαγραφής κόµβου µε δύο παιδιά // βρες το διάδοχο του κόµβου προς διαγραφή(current) Node successor = getsuccessor(current); // σύνδεσε τον πατέρα του current µετοδιάδοχοτουcurrent if(current == root) //διαγραφή ρίζας root = successor; if(isleftchild) //ο current είναι αριστερό παιδί.leftchild = successor;.rightchild = successor; // σύνδεσε τον διάδοχο µε τo αριστερό παιδί του current successor.leftchild = current.leftchild; 11
Χρονική πολυπλοκότητα αλγορίθµων για δυαδικά δέντρα αναζήτησης( Α) Η εισαγωγή και διαγραφή ενός κόµβου σ ένα Α βασίζονται στον αλγόριθµο αναζήτησης Οαπαιτούµενος χρόνος για την αναζήτηση ενός κόµβου είναι ανάλογος του ύψους h του δέντρου. υαδικό δέντρο µε Νκόµβους έχει ύψος τουλάχιστον log(n+1) Όλες οι πράξεις ενός Α εέχουν στην καλύτερη περίπτωση πολυπλοκότητα O(logN) και στη χειρότερη Ο(Ν) h=2 h=6 Ο(Ν) Α µε τοίδιοπλήθοςκόµβων Ο(logΝ) Η χρονική πολυπλοκότητα όλων των πράξεων ενός Α εξαρτάται από το σχήµα του 12