Skip to content
Snippets Groups Projects
AVLTree.java 7.23 KiB
Newer Older
public class AVLTree<T extends Comparable<T>> {

    private class Node {		// ein Knoten im Baum
	T    value;			// Wert am Knoten
        Node left;			// Wurzel linker Unterbaum
	Node right;			// Wurzel rechter Unterbaum
	int  balance;			// hoehe(linker Unterbaum) - hoehe(rechter Unterbaum)

	Node(T val) {
	    value = val;
	    left = null;
	    right = null;
	    balance = 0;
	}
    }

    private Node root;			// Wurzel des gesamten AVL-Baums
    private int size;			// Anzahl Knoten im AVL-Baum

    public AVLTree() {
        root = null;
	size = 0;
    }

    public int size() {
        return size;
    }

    public boolean contains(T elem) {
        Node node = root;
        int  diff;

	while (node != null) {
	    diff = node.value.compareTo(elem);
	    if (diff == 0) {
	        return true;
	    }
	    else if (diff>0) {
	        node = node.left;
	    }
	    else {
	        node = node.right;
	    }
	}

	return false;
    }

    public void insert(T elem) {
      insert(root,null,elem);
    }

    /*
     * Rekursives Einfuegen in einen Unterbaum.
     *
     * node: Der Wurzelknoten des Baumes bzw. Unterbaums, in den eingefuegt wird.
     * father: Vaterknoten von node, node ist linker oder rechter Sohn von father
     * elem: Der einzufuegende Wert
     *
     * return: true, falls der Unterbaum durch das Einfuegen hoeher geworden ist,
     *    sonst false
     */
    private boolean insert(Node node, Node father, T elem) {
      int     diff;
      boolean grown;
      int     balanceDelta;

      if (node==null) {			// am leeren Unterbaum angekommen
        node=new Node(elem);		// dann wird hier ein neuer Knoten erzeugt
        if (father==null) {			// nur moeglich, wenn der root==null also insgesamt leerer Baum
	        root = node;			// dann besteht der Baum jetzt aus genau einem Knoten
        }					// ansonsten muessen wir untersuchen, ob der neue Knoten
        else if (father.value.compareTo(elem) > 0) {	// linker oder rechter Sohn seines Vaters wird.
	        father.left = node;		// Wert im Vater ist groesser, dann wird neuer Knoten
        }					// linker Sohn
        else {				// Wert im Vater ist kleiner, dann wird neuer Knoten
	        father.right = node;		// rechter Sohn
        }
        
        size++;				// Baum hat insgesamt einen Knoten mehr
        return true;			// Unterbaum mit node als Wurzel ist gewachsen
       }

	// Wenn wir hier angekommen sind, gilt node != null.
	// Wir muessen also in einen der beiden Unterbaeume von node einfuegen

      diff = node.value.compareTo(elem);	
      if (diff == 0) {			// Wert elem schon im Baum vorhanden
  	    return false;			// kein Einfuegen!
      }
      else if (diff > 0) {			// node.value > elem ==> in linken Unterbaum einfuegen
        grown = insert(node.left,node,elem);
        balanceDelta = 1;
      }
      else {					// node.value < elem ==> in rechten Unterbaum einfuegen
        grown = insert(node.right,node,elem);
        balanceDelta = -1;
      } 

      if (grown) {				// wenn der unterbaum von node, in den wir eingefuegt haben,
        node.balance += balanceDelta;	// gewachsen ist, dann aendert sich der balance-Wert
      }					// am Knoten node

      if (!grown || node.balance==0) {	// Keine Hoehenaenderung oder Fall 2 (Folie 253)
        return false;			// Baum mit Wurzel node ist insgesamt nicht gewachsen
      }

	// Wenn wir hier angekommen sind, ist der Baum mit node als Wurzel gewachsen.
	// Es liegt entweder Fall 1 oder Fall 3 vor.

      if (node.balance==1 || node.balance==-1) {	// AVL Kriterium noch erfuellt
	    return true;				// Fall 1 (Folie 250)
	}						// Der Baum ist auf jeden Fall gewachsen

	// Wenn wir hier angekommen sind, ist der Baum mit Wurzel node gewachsen und das
	// AVL-Kriterium ist verletzt.
        if (node.balance==2) {
	    if (node.left.balance==1) {
	        rotateLeftLeft(node);		// Fall 3a (Folie 258)
	    }
	    else {
          rotateLeftRight(node); // LR-Rotation			// Fall 3b (Folie 261)
	    }
	}
	else {
	    if (node.right.balance==-1) {
	        rotateLeftRightSymmetric(node);			// Fall symmetrisch zu 3a
	    }
	    else {
	        rotateLeftRightSymmetric(node); // RL-Rotation			// Fall symmterisch zu 3b
	    }
	}

        // Ausgleichsrotationen fuehren beim Einfuegen stets dazu, dass der Unterbaum mit
	// node als Wurzel nicht gewachsen ist.

	return false;
    }

    private void rotateLeftLeft(Node w) {	// vgl. Folie 258
      Node v  = w.left;
      Node a1 = v.left;
      Node a2 = v.right;
      Node b  = w.right;
      T    tmpval;
      Node tmpnode;

      tmpval = w.value;			// Werte in den Knoten tauschen
      w.value = v.value;
      v.value = tmpval;

      tmpnode = w;				// Verweise auf Knoten tauschen
      w = v;              // damit anschliessend wieder gilt:
      v = tmpnode;				// in Knoten v liegt v.value und in w liegt w.value

      v.left = a1;				// Verweise umhaengen
      v.right = w;
      w.left = a2;
      w.right = b;

      v.balance = 0;      // Unterbaeume ab v und w sind jetzt auf jeden Fall
      w.balance = 0;			// vollstaendig ausgeglichen
    }
    
    private void rotateLeftRight(Node w) {
      Node v  = w.left;
      Node u  = v.right;
      Node a1 = v.left;
      Node a2 = u.left;
      Node a3 = u.right;
      Node b  = w.right;
      T    tmpval;
      Node tmpnode;
      
      tmpval = w.value;			// Werte in den Knoten tauschen
      w.value = u.value;
      u.value = tmpval;
      
      tmpnode = w;				// Verweise auf Knoten tauschen
      w = u;              // damit anschliessend wieder gilt:
      u = tmpnode;				// in Knoten v liegt v.value und in w liegt w.value
      
      u.left = v;
      u.right = w;
      
      v.left = a1;
      v.right = a2;
      
      w.left = a3;
      w.right = b;
      
      u.balance = 0;
      v.balance = 0;
      w.balance = 0;
    }
    
    private void rotateLeftLeftSymmetric(Node w) {	// vgl. Folie 258
      Node v  = w.right;
      Node a1 = v.right;
      Node a2 = v.left;
      Node b  = w.left;
      T    tmpval;
      Node tmpnode;

      tmpval = w.value;			// Werte in den Knoten tauschen
      w.value = v.value;
      v.value = tmpval;

      tmpnode = w;				// Verweise auf Knoten tauschen
      w = v;              // damit anschliessend wieder gilt:
      v = tmpnode;				// in Knoten v liegt v.value und in w liegt w.value

      v.right = a1;				// Verweise umhaengen
      v.left = w;
      w.right  =  a2;
      w.left = b;

      v.balance = 0;      // Unterbaeume ab v und w sind jetzt auf jeden Fall
      w.balance = 0;			// vollstaendig ausgeglichen
    }
    
    
    private void rotateLeftRightSymmetric(Node w) {
      Node v  = w.right;
      Node u  = v.left;
      Node a1 = v.right;
      Node a2 = u.right;
      Node a3 = u.left;
      Node b  = w.left;
      T    tmpval;
      Node tmpnode;
      
      tmpval = w.value;			// Werte in den Knoten tauschen
      w.value = u.value;
      u.value = tmpval;
      
      tmpnode = w;				// Verweise auf Knoten tauschen
      w = u;              // damit anschliessend wieder gilt:
      u = tmpnode;				// in Knoten v liegt v.value und in w liegt w.value
      
      u.left = v;
      u.right = w;
      
      v.right = a1;
      v.left = a2;
      
      w.right = a3;
      w.left = b;
      
      u.balance = 0;
      v.balance = 0;
      w.balance = 0;
    }
}