Vorlesung Datenstrukturen Balancieren eines Suchbaums Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 396 Baumgestalt vs. Zeitkomplexität Vorteile eines ausgeglichenen binären Suchbaums Suchoperation besitzt Zeitkomplexität O(log N) bei N Knoten Einfügeoperation besitzt Zeitkomplexität O(log N) bei N Knoten Potenzielle Problematik Speziell beim wiederholten Einfügen neuer Knoten aber auch beim Löschen (mit der Methode „Löschen durch Verschmelzen“) hatten wir festgestellt, dass ein Suchbaum degenerieren kann. Als Konsequenz sinkt die Effizienz der Such- und Einfügeoperationen merklich. Abhilfe Wiederherstellen eines ausgeglichenen Baumes durch Balancieren des Baumes. Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 397 Balancierte Bäume Balancierter Baum Ein binärer Baum wird als balanciert bezeichnet, wenn die absolute Höhendifferenz der beiden Teilbäume jedes Knotens maximal Eins beträgt. Perfekt balancierter Baum Ein balancierter Baum wird als perfekt balanciert angesehen, wenn sich alle Blätter auf höchstens zwei benachbarten Ebenen befinden. Voraussetzung für das Balancieren Binäre Suchbäume haben die Eigenschaft, dass jeder Knoten eines Baumes die Wurzel des Baumes repräsentieren kann. Da Bäume rekursiv definiert sind, gilt dies natürlich auch für alle Teilbäume, also alle Knoten eines Baumes. Konsequenz Knoten können unter Beibehaltung des Suchbaumkriteriums im Baum verschoben werden. Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 398 Balancieren eines Baums (1) Eigenschaft von Suchbäumen Jeder Knoten eines Suchbaumes kann unter Beibehaltung des Suchbaumkriteriums innerhalb des Baumes verschoben werden. Rotation Operation zur Vertauschung des Wurzelknotens eines (Teil-)Baums mit einem seiner beiden Nachfolger. Forderung an Rotationsoperation Die Ordnung zwischen den Schlüsselwerten muss bewahrt werden. Varianten Linksrotation Rechtsrotation Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 399 Rechtsrotation Bedeutung node* rotateRight( node* n ) { if ( n ) { Ein Knoten wird durch seinen linken Kindknoten node* temp = n->left; ersetzt ➔ der Kindknoten rotiert nach rechts. if ( temp ) {// linkes Kind vorhanden? Bedingung n->left = temp->right; temp->right = n; Existenz eines nichtleeren linken Teilbaums. n = temp; } Konsistenzbewahrung } Der ersetzte Knoten (der größer als sein bisheriger return n; // neue Teilbaumwurzel linker Kindknoten ist) wird rechter Nachfolger seines } (ehemals) linken Kindknotens (Rotation nach rechts). Beachte, dass die neue Teilbaumwurzel im Der rechte Teilbaum des ehemaligen linken Kindknotens wird linker Nachfolger des ersetzten Knotens. Dr. Frank Seifert Anschluss noch genau an der Stelle in den übergeordneten Knoten eingehängt werden muss, an der sich die alte Teilbaumwurzel befand. Vorlesung Datenstrukturen - Sommersemester 2016 Folie 400 Linksrotation Bedeutung node* rotateLeft( node* n ) { if ( n ) { Ein Knoten wird durch seinen rechten Kindknoten node* temp = n->right; ersetzt ➔ der Kindknoten rotiert nach links. if ( temp ) { // rechtes Kind vorhanden? n->right = temp->left; Bedingung temp->left = n; Existenz eines nichtleeren rechten Teilbaums. n = temp; } Suchbaumerhaltung } Der ersetzte Knoten (der kleiner als sein bisheriger rechter Kindknoten ist) wird linker Nachfolger seines return n; // neue Teilbaumwurzel } (ehemals) rechten Kindknotens (Rotation nach links). Beachte, dass die neue Teilbaumwurzel im Anschluss Der linke Teilbaum des ehemaligen rechten Kindknotens wird rechter Nachfolger des ersetzten Knotens. Dr. Frank Seifert noch genau an der Stelle in den übergeordneten Knoten eingehängt werden muss, an der sich die alte Teilbaumwurzel befand. Vorlesung Datenstrukturen - Sommersemester 2016 Folie 401 Balancieren eines Baumes (2) Mit Hilfe von Rotationen transformieren wir den zu balancierenden Baum zunächst in die Form einer linearen Liste. Diese stellt eine einheitliche Ausgangsform („Rückgrat“) für alle Bäume dar und bildet die Basis für das anschließende eigentliche Balancieren. node* createBackbone( node* n ) { if ( n ) { while ( n->left ) // Rotiere jeweilige Wurzel nach rechts n = rotateRight( n ); // bis diese keinen linken Teilbaum mehr besitzt node* temp = n, *save = n; temp = temp->right; while ( temp ) { if ( temp->left ) { // wenn temp ein linkes Kind besitzt temp = rotateRight(temp);// rotiere temp nach rechts und save->right = temp; // hänge den rotierten Knoten in den Baum } else { save = temp; temp = temp->right; } // Fahre mit rechtem Kind fort } } return n; // Rückgabe der Wurzel des „Listen“-Baumes } Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 402 Balancieren eines Baumes (3) Zweiter Schritt Wir transformieren den „Listenbaum“ in einen perfekt balancierten Baum. DSW-Algorithmus (Day, Stout, Warren) In mehreren Durchläufen wird, jeweils mit der Wurzel beginnend, jeder zweite (rechte) Knoten linksrotiert: Im ersten Durchlauf ergibt sich die Anzahl der auszuführenden Linksrotationen aus der Differenz der vorhandenen Knotenanzahl N des Baumes zur Knotenanzahl M des nächst kleineren vollständig gefüllten binären Baumes. M = 2"lg(N +1)# −1 Die Anzahl der auszuführenden Linksrotationen der nächsten Durchläufe ergibt sich danach direkt aus der Knotenanzahl€des jeweils nächst kleineren vollständig gefüllten binären Baumes. Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 403 Balancieren eines Baumes (4) node* perfectTreePass(node* root, int M) {// M ist Anzahl der Rotationen node* temp, * prev; node* save = root->right, node* next = save->right; for ( int i = 0; i < M; i++ ) { // M-Mal wird jeder zweite Knoten des Baumes if ( i ) // nach links rotiert prev->right = rotateLeft( temp ); else root = rotateLeft( root ); // Bei Rotation um Wurzel ergibt sich neue Wurzel temp = next; // Zuweisen des übernächsten (vor der Rotation) prev = save; // Folgeknotens if (next) { // Weiterrücken um zwei Knoten jeweils save = next->right; if ( save ) next = save->right; Achtung: } Es werden nicht alle Ausnahmesituationen abgefangen! } return root; } Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 404 Balancieren eines Baumes (5) Zeitkomplexität Die Generierung des Backbone benötigt bestenfalls N Schritte (wenn der Baum schon ein Backbone ist) und schlimmstenfalls 2N–1 Schritte mit N–1 Rotationen ➔ Komplexität O(N). Der DSW-Algorithmus benötigt im ersten Durchlauf N–M Schritte. In den weiteren Durchläufen werden jeweils so viele Rotationen durchgeführt, wie sich Knoten im jeweils nächst kleineren ausgeglichenen vollen Baum befinden: lg(M +1)−1 (2 lg(M +1)−1 −1) + (2 lg(M +1)−2 −1)...+ 7 + 3 + 1 = ∑ (2 i −1) = M − lg(M + 1) Da M die Knoten- i=1 Damit ergibt sich insgesamt folgende Anzahl an Rotationen (Rechenschritte): € N − M + (M − lg(M + 1)) = N − lg(M + 1) = N − #lg(N + 1)$ anzahl des nächst kleineren vollständig gefüllten Baumes ist, kann lg(M+1) durch lg(N+1) ersetzt werden. Konsequenz € weist die komplette Neubalancierung eines Baumes eine Zeitkomplexität von O(N) auf. Insgesamt Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 405 Ende der Vorlesung Dr. Frank Seifert Vorlesung Datenstrukturen - Sommersemester 2016 Folie 406