AVL Tree

misaki

Mitglied
Hallo!!

Ich habe mal wieder mit der wöchentlichen Aufgabe zu kämpfen ... ;)
Dieses Mal soll ich eine Klasse für einen AVL-Baum implementieren, bestehend aus int-Zahlen zu dem nur Elemente hinzugefügt oder gelöscht werden können.
Jeder Knoten soll die Höheninformation beinhalten. Die Gesamthöhe soll mittels der Methode hoehe() wiedergegeben werden und der 2erlogarithmus der Anzahl der Knoten mit der Methode logAnzKnoten().

Folgendermaßen sieht meine Datei bisher aus:
Java:
public class MyAVLTree {
 
    private Node root; // Wurzelknoten
    
    public MyAVLTree() {              
    // Konstruktor; leerer Baum  
        root = null;
    }
 
    public void insert(int item) {
    //fuegt item ein; unwirksam falls vorhanden
        root = insert(root, item);      
    }
    
    private Node insert(Node n, int i) {
    // private Methode um rekursiv verwenden zu können
        if (n == null) {
            return new Node(new Node(), i, new Node()); // Baum leer, füge einfach hinzu
        }
        else if (i < n.data) { // links einfügen
            n.left = insert(n.left, i);
            n = updateHeight(n); // Höheninformationen updaten
            if (n.left.height - n.right.height == 2) { // rechtsrotation notwendig
                if (i < n.left.data) {
                    n = rotateRight(n);
                }
                else {
                    n.left = rotateLeft(n.left);
                    n = rotateRight(n);
                }
				return n;
            }
        }
        else if (i > n.data) { // rechts einfügen
            // analog
            n.right = insert(n.right, i);
            n = updateHeight(n);
            if (n.right.height - n.left.height == 2) {
                if (i > n.right.data) {
                    n = rotateLeft(n);
                }
                else {
                    n.right = rotateRight(n.right);
                    n = rotateLeft(n);
                }
				return n;
            }
        }
        else { // item vorhanden, tue nichts
            return n;
        }
		return n; // für den Compiler
    }
    
    private Node rotateRight (Node n) {
    // Rechtsrotation
        Node temp = new Node();
        temp = n.left;
        n.left = temp.right;
        temp.right = n;
        return temp;
    }
    
    private Node rotateLeft (Node n) {
    // Linksrotation
        Node temp = new Node();
        temp = n.right;
        n.right = temp.left;
        temp.left = n;
        return temp;
    }
 
    public void delete(int item) {
    //loescht item, falls vorhanden; sonst unwirksam
        if (isElem(item)) {
            root = delete(root, item);
        }
    }
    
    private Node delete (Node n, int i) {
    // private Methode für rekursiven Aufruf, analog zu "insert"
        if (isElem(i)) {
            if (i < n.data) { // links löschen
                n.left = delete(n.left, i);
                n = updateHeight(n);
                if (n.left.height - n.right.height == 2) {
                    if (i < n.left.data) {
                        n = rotateRight(n);
                    }
                    else {
                        n.left = rotateLeft(n.left);
                        n = rotateRight(n);
                    }
					return n;
                }

            }
            else if (i > n.data) { // rechts löschen
                n.right = delete(n.right, i);
                n = updateHeight(n);
                if (n.right.height - n.left.height == 2) {
                    if (i > n.right.data) {
                        n = rotateLeft(n);
                    }
                    else {
                        n.right = rotateRight(n.right);
                        n = rotateLeft(n);
                    }
				    return n;
                }
            }
        }
        return null; // nur für den Compiler!!!!
    }
 
    public boolean isElem(int item) {
    // liefert true, wenn item im AVL-Baum schon vorhanden ist
        return isElem(root, item);      
    }
    
    private boolean isElem(Node n, int i) {
    // private Methode für rekursiven Aufruf
        if (n == null) {
            return false; // Baum leer - Element nicht vorhanden
        }
        else if (n.data == i) {
            return true; // Element vorhanden
        }
        else {
            return (isElem(n.left, i) || isElem(n.right, i));
        }
    }
 
    public double logAnzKnoten() {
    //gibt 2er-Logarithmus der Anzahl der aktuell im Baum vorhandenen Knoten
        int anzKnoten = AnzKnoten(root);
        // log_b(a)=ln(a)/ln(b) wobei b ... Basis
        return (Math.log(anzKnoten)/Math.log(2));
    }
    
    private int AnzKnoten(Node n) {
        if (n == null) {
            return 0;
        }
        else {
            return (1 + AnzKnoten(n.left) + AnzKnoten(n.right));
        }
    }
    
    public int hoehe() {
    //gibt aktuelle Hoehe des Baums 
        return root.height;
    }
    
    private Node updateHeight(Node n) {
    // um die Höhe upzudaten wird der Baum neu aufgebaut, durch den Konstruktor ändert sich die Instanzvariable
        n = new Node(updateHeight(n.left), n.data, updateHeight(n.right));
        return n;
    }
    
    // ------------- Ausgeben des Baums zur Kontrolle
    
    public void printTree(){                        //wrapper
        printTree(root, 1);
    }
   
    private void printTree(Node n, int h){      //recursion
        if (n!=null){                                //like inorder
            printTree(n.right, h+1);        
            printNode(n,h);
            printTree(n.left, h+1);}
    }
 
    private void printNode(Node n, int h){
        //single node
        for(int i=0;i<h;i++) System.out.print(" ");
        System.out.println(n.data);
    }
    
    //-------------- innere Klasse für Knoten
    private class Node {
        int data;
        Node left, right;
        int height; // Höheninformation
        
        Node() {
            height = -1; // Baum leer --> Höhe = -1
        }
        Node(Node l, int i, Node r) {
            data = i;
            left = l;
            right = r;
            height = (1 + Math.max(left.height, right.height));
        }
    }
}

nun - ich denke ich liege mit meinen überlegungen ganz richtig.
deshalb komme ich auch nicht darauf, warum ich beim testen (hinzufügen, löschen) immer eine nullpointerexception habe. das einzige wo ich mir relativ sicher bin ist, dass es etwas mit der höheninformation zu tun hat (durch die zeilenangaben).

hilfe??
 
Zuletzt bearbeitet:

Haave

Top Contributor
Uff, 200 Zeilen Code… könntest du die Zeilennummer der Null Pointer Exception verraten? ;)

Übrigens brauchst du root im Konstruktor nicht explizit auf null zu setzen, das wird sowieso null sein, wenn es sonst nichts anderes zugewiesen bekommt.
 

misaki

Mitglied
so, die npe is jetz beseitigt (hatte bei updateheight den fall des leeren baums nicht beachtet) - aber jetzt wird mit meinem testprogramm endlos der baum ausgedruckt - und das auch noch falsch:noe:

Java:
public class MyAVLTree {
 
    private Node root; // Wurzelknoten
    
    public MyAVLTree() {              
    // Konstruktor; leerer Baum  
        root = null;
    }
 
    public void insert(int item) {
    //fuegt item ein; unwirksam falls vorhanden
        root = insert(root, item);      
    }
    
    private Node insert(Node n, int i) {
    // private Methode um rekursiv verwenden zu können
        if (n == null) {
            return new Node(new Node(), i, new Node()); // Baum leer, füge einfach hinzu
        }
        else if (i < n.data) { // links einfügen
            n.left = insert(n.left, i);
            n = updateHeight(n); // Höheninformationen updaten
            if (n.left.height - n.right.height == 2) { // rechtsrotation notwendig
                if (i < n.left.data) {
                    n = rotateRight(n);
                }
                else {
                    n.left = rotateLeft(n.left);
                    n = rotateRight(n);
                }
                return n;
            }
        }
        else if (i > n.data) { // rechts einfügen
            // analog
            n.right = insert(n.right, i);
            n = updateHeight(n);
            if (n.right.height - n.left.height == 2) {
                if (i > n.right.data) {
                    n = rotateLeft(n);
                }
                else {
                    n.right = rotateRight(n.right);
                    n = rotateLeft(n);
                }
                return n;
            }
        }
        else { // item vorhanden, tue nichts
            return n;
        }
        return n; // für den Compiler
    }
    
    private Node rotateRight (Node n) {
    // Rechtsrotation
        Node temp = new Node();
        temp = n.left;
        n.left = temp.right;
        temp.right = n;
        return temp;
    }
    
    private Node rotateLeft (Node n) {
    // Linksrotation
        Node temp = new Node();
        temp = n.right;
        n.right = temp.left;
        temp.left = n;
        return temp;
    }
 
    public void delete(int item) {
    //loescht item, falls vorhanden; sonst unwirksam
        if (isElem(item)) {
            root = delete(root, item);
        }
    }
    
    private Node delete (Node n, int i) {
    // private Methode für rekursiven Aufruf, analog zu "insert"
        if (isElem(i)) {
            if (i < n.data) { // links löschen
                n.left = delete(n.left, i);
                n = updateHeight(n);
                if (n.right.height - n.left.height == 2) { // nach löschen könnte der rechte unterbaum zu groß sein
                    if (i > n.right.data) {
                        n = rotateLeft(n);
                    }
                    else {
                        n.right = rotateRight(n.right);
                        n = rotateLeft(n);
                    }
                    return n;
                }
            }
            else if (i > n.data) { // rechts löschen, analog
                n.right = delete(n.right, i);
                n = updateHeight(n);
				if (n.left.height - n.right.height == 2) {
                    if (i < n.left.data) {
                        n = rotateRight(n);
                    }
                    else {
                        n.left = rotateLeft(n.left);
                        n = rotateRight(n);
                    }
                    return n;
                }
            }
        }
		return n; // gib n zurueck, wenn i nicht vorkommt (es wird also nix gelöscht)
    }
 
    public boolean isElem(int item) {
    // liefert true, wenn item im AVL-Baum schon vorhanden ist
        return isElem(root, item);      
    }
    
    private boolean isElem(Node n, int i) {
    // private Methode für rekursiven Aufruf
        if (n == null) {
            return false; // Baum leer - Element nicht vorhanden
        }
        else if (n.data == i) {
            return true; // Element vorhanden
        }
        else {
            return (isElem(n.left, i) || isElem(n.right, i));
        }
    }
 
    public double logAnzKnoten() {
    //gibt 2er-Logarithmus der Anzahl der aktuell im Baum vorhandenen Knoten
        int anzKnoten = AnzKnoten(root);
        // log_b(a)=ln(a)/ln(b) wobei b ... Basis
        return (Math.log(anzKnoten)/Math.log(2));
    }
    
    private int AnzKnoten(Node n) {
        if (n == null) {
            return 0;
        }
        else {
            return (1 + AnzKnoten(n.left) + AnzKnoten(n.right));
        }
    }
    
    public int hoehe() {
    //gibt aktuelle Hoehe des Baums 
        return root.height;
    }
    
    private Node updateHeight(Node n) {
	// um die Höhe upzudaten wird der Baum neu aufgebaut, durch den Konstruktor ändert sich die Instanzvariable
		if (n == null) {
			return new Node();
		}
		else {	
			return new Node(updateHeight(n.left), n.data, updateHeight(n.right));
		}
    }
    
    // ------------- Ausgeben des Baums zur Kontrolle
    
    public void printTree(){                        //wrapper
        printTree(root, 1);
    }
   
    private void printTree(Node n, int h){      //recursion
        if (n!=null){                                //like inorder
            printTree(n.right, h+1);        
            printNode(n,h);
            printTree(n.left, h+1);}
    }
 
    private void printNode(Node n, int h){
        //single node
        for(int i=0;i<h;i++) System.out.print(" ");
        System.out.println(n.data);
    }
    
    //-------------- innere Klasse für Knoten
    private class Node {
        int data;
        Node left, right;
        int height; // Höheninformation
        
        Node() {
            height = -1; // Baum leer --> Höhe = -1
        }
        Node(Node l, int i, Node r) {
            data = i;
            left = l;
            right = r;
            height = (1 + Math.max(left.height, right.height));
        }
    }
}

Java:
public class Bsp09 {
	public static void main(String[] args) {
		MyAVLTree baum = new MyAVLTree();
		System.out.println("---- fuege 0 hinzu");
		baum.insert(0);
		baum.printTree();
		System.out.println("---- fuege 2 hinzu");
		baum.insert(2);
		baum.printTree();
		System.out.println("---- fuege 3 hinzu");
		baum.insert(3);
		baum.printTree();
		System.out.println("---- fuege 7 hinzu");
		baum.insert(7);
		baum.printTree();
		System.out.println("---- fuege 1 hinzu");
		baum.insert(1);
		baum.printTree();
		System.out.println("---- loesche 2");
		baum.delete(2);
		baum.printTree();
		System.out.println("Hoehe:" + baum.hoehe());
		System.out.println("log2 von Anz. Knoten:" + baum.logAnzKnoten());

	}
}
 

Dekker

Bekanntes Mitglied
Ok, fangen wir mal an:

Es beginnt schon alleine damit, dass du beim einfügen einen Knoten mit 2 leeren Kinderknoten rechts und links direkt erzeugst. Bei dennen ist dann Data = 0 weil das bei int so initialisiert wird. Aus diesem Grund macht es keinen Sinn immer wenn ne neue Node erzeugt werden soll 3 zu erzeugen.

Java:
Node(Node l, int i, Node r) {
            data = i;
            left = l;
            right = r;
            height = (1 + Math.max(left.height, right.height));
        }

Zusammen damit das er bei ausgeben auch in die leeren Knoten l und r geht -> kein Wunder das da was schiefgeht. Also musst du das in der Rekursion prüfen, deine Funktion müsste dann etwa so wie folgende aussehen:

Java:
 private void printTree(Node n, int h){    
        if (n!=null){                               
            if(n.right.getHeight() =! -1) printTree(n.right, h+1);        
            printNode(n,h);
            if(n.left.getHeight() =! -1) printTree(n.left, h+1);
    }

Dabei passiert übrigens auch beim einfügen Mist. Wenn du 0 eingefügt hast, und dann 2 einfügst, überprüft er erst ob root == null gilt. Offensichtlich nein, also springt er bis zu i > n.data, ruft insert(n.right,i) auf und geht rekursiv eine Ebene runter. Soweit so gut. Was du jetzt willst, ist das er das an n.right einfügt. Allerdings ist doch n.right gar nicht 0 sondern einfach dein leerer Dummyknoten -> Er überprüft wieder n==null? Da dies wieder nicht zutrifft geht er wieder nach rechts mit n.right und fügt den Knoten jetzt hier ein, da jetzt kein Dummyknoten anhängt und beim nächsten Aufruf von insert(n.right,i) n == null gilt. Ergo baust du den ganzen Baum falsch auf.

Allgemein möchte ich dir ans Herzlegen auf eine IDE umzusteigen. Debugger sind gerade bei solch komplizierten Datenstruckturen Gold wert.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
M Parse-Tree eines statements darstellen Java Basics - Anfänger-Themen 0
RudiRüssel Tree Java Basics - Anfänger-Themen 3
Vince42 NIO File Tree in XML umwandeln Java Basics - Anfänger-Themen 10
S Binary Search Tree - Nummerierung in Inorder Java Basics - Anfänger-Themen 5
P expression tree Java Basics - Anfänger-Themen 4
T Expression Tree.. identifier + Grundaufbau? Java Basics - Anfänger-Themen 2
A Anzahl nodes in einem Tree Java Basics - Anfänger-Themen 2
L Linksrotation RedBlack Tree Java Basics - Anfänger-Themen 3
L Binear Tree Java Basics - Anfänger-Themen 5
L File Tree Node ausgeben Java Basics - Anfänger-Themen 2
L File Tree rekursiv Java Basics - Anfänger-Themen 10
V libssrckdtree-j Generic k-d tree Java library - weiss nicht wo des hin soll Java Basics - Anfänger-Themen 2
T Java Tree Frage Java Basics - Anfänger-Themen 2
P Tree aus XML Daten aufbauen Java Basics - Anfänger-Themen 9
R Tree gefüllt mit den Directory Java Basics - Anfänger-Themen 2
B API für Tree Java Basics - Anfänger-Themen 4
M Pfade in Tree einbinden Java Basics - Anfänger-Themen 2
R Multiway Tree Java Basics - Anfänger-Themen 3
G tree rekursiv Java Basics - Anfänger-Themen 8
R Tree + bilder ? Java Basics - Anfänger-Themen 7
M Minimal Spanning Tree mit Greedy Java Basics - Anfänger-Themen 2
J Erweitern eines Tree-Pfades? Java Basics - Anfänger-Themen 3

Ähnliche Java Themen

Neue Themen


Oben