import java.io.IOException;

class Element implements Comparable{
    Character Inhalt;
    Element(Character c){ Inhalt = c;}
    public int compareTo(Object o){
        Element e = (Element) o;
        return Inhalt.compareTo(e.Inhalt);} 
    }


class Knoten{
    Element e;
    Knoten o, l, r;
    Knoten(Element el){ e = el;}
    Knoten(Element el, Knoten oben){ e = el; o = oben;}
    public String toString(){return (e.Inhalt).toString();}
    }

class SuchBaum{
    private Knoten wurzel = null;

    boolean istLeer(){ return wurzel == null;}


     void einfuegen(Element e){
		if (wurzel == null) wurzel = new Knoten(e);
		else rekEinfuegen(e, wurzel);
		}

	void rekEinfuegen(Element e, Knoten bp){
					// Beginn: Code für Implementierungsinvariante
		if (e.compareTo(bp.e) == 0){
			Knoten neu = new Knoten(e, bp);
			if (bp.l != null) bp.l.o = neu;
			neu.l = bp.l;
			bp.l = neu;
			return;
			} 		// Ende des Codes für Implementierungsinvariante
		if (e.compareTo(bp.e) < 0) 
			if (bp.l == null) bp.l = new Knoten(e, bp);
			else rekEinfuegen(e, bp.l);
		else
			if (bp.r == null) bp.r = new Knoten(e, bp);
			else rekEinfuegen(e, bp.r);
		}
        
    Knoten sucheMin(){ return sucheMin(wurzel);}
	
    Knoten sucheMin(Knoten kp){
			if (kp == null) return kp;
			while (kp.l != null) kp = kp.l;
			return kp;
			}
        
    Knoten sucheMax(){ return sucheMax(wurzel); }    
    
    Knoten sucheMax(Knoten kp){
        if (kp == null) return kp;
        while (kp.r != null) kp = kp.r;
        return kp;
        }  
    
        
    Knoten nachfolger(Knoten kp){
			if (kp == null) return kp;
			if (kp.r != null) return sucheMin(kp.r);
			Knoten oben = kp.o;
			while((oben != null) && (oben.r == kp)) {
				kp = oben; 
				oben = kp.o;
				}
			return oben;
			}  
      
        
    Knoten vorgaenger(Knoten kp){
        if (kp == null) return kp;
        if (kp.l != null) return sucheMax(kp.l);
        Knoten Oben = kp.o;
        while((Oben != null) && (Oben.l == kp)) {kp = Oben; Oben = kp.o;}
        return Oben;
        }        

    void loescheKnoten(Knoten kp){
	if (kp == null) return; 
	if (kp.l == null || kp.r == null) loesche1(kp); 
	else loesche2(kp);
        }

    void loesche1(Knoten sohn){  // Loescht Knoten mit max. einem Sohn 
      // Bestimme den Enkel - kann null sein 
			Knoten enkel = (sohn.l == null) ? sohn.r : sohn.l;	 
			if (sohn == wurzel)  { wurzel = enkel;  wurzel.o = null; return; }
		      // Ab hier ist klar: Vater existiert 
			Knoten vater = sohn.o;	 
		      //Verbinde Vater zum Enkel 
			if (vater.l == sohn) vater.l = enkel; else vater.r = enkel; 
		      // Verbinde Enkel zum Vater 
			if (enkel != null) enkel.o = vater; 	
        }

    void loesche2(Knoten kp){
				Knoten min = sucheMin(kp.r);
			      // Beginn: Berücksichtigung von Duplikaten 
				min.l = kp.l;
				if (min.l != null) min.l.o = min; // Vaterknoten korrekt?		
				while(min.e.compareTo(min.o.e) == 0) min = min.o;	
				kp.l = min.l;
				if (kp.l != null) kp.l.o = kp;	// Vaterknoten korrekt? 
				min.l = null; // Damit loesche1 anwendbar wird 
			      // Ende: Berücksichtigung von Duplikaten 
				kp.e = min.e; // Kopiere Inhalt nach oben
				loesche1(min);
        }	

    Knoten suche(Element e){ return rekSuche(e, wurzel);}
			Knoten rekSuche(Element e, Knoten bp){
				if (bp == null) return null;
				if (e.compareTo(bp.e) == 0) return bp;
				if (e.compareTo(bp.e) < 0) return rekSuche(e, bp.l);
				else return rekSuche(e, bp.r);
				}

    
    void ausgabeVorw(){
    	System.out.println("--------- Ausgabe vorwärts -------------");
        Knoten kp = sucheMin();
        while (kp != null) { 
           System.out.print(kp);
           kp = nachfolger(kp);
           }
        System.out.println();
        } 
    
    void ausgabeRueckw(){
        System.out.println("--------- Ausgabe rückwärts -------------");
        Knoten kp = sucheMax();
        while (kp != null) { 
           System.out.print(kp);
           kp = vorgaenger(kp);
           }
        System.out.println();
        } 
        
    void DumpKnoten(Knoten z){
        System.out.println("Ausgabe von Knoten: " + z);
        if (z.o != null) System.out.println("Vorgänger-Knoten: " + z.o);
         else System.out.println("Kein Vorgänger-Knoten!");
        if (z.l != null) System.out.println("Linker Nachfolger-Knoten: " + z.l);
         else System.out.println("Kein linker Nachfolger-Knoten!"); 
        if (z.r != null) System.out.println("Rechter Nachfolger-Knoten: " + z.r);
         else System.out.println("Kein rechter Nachfolger-Knoten!");    
        }  
        
    void DumpTeilBaum(Knoten z, int Level){
        if (z == null) return;
        System.out.println("----------------------");
        System.out.print("Level: " + Level + " ");
        DumpKnoten(z);
        if (z.l != null) DumpTeilBaum(z.l, Level + 1);
        if (z.r != null) DumpTeilBaum(z.r, Level + 1);
        } 
        
    void DumpBaum(){
        System.out.println("--------- Dump Baum -------------");
        DumpTeilBaum(wurzel, 0);    
        }         
}


public class BaumS {

    public static void main(String args[]) {
        System.out.println("Baum Tester");
        System.out.println("");
        String Beispiel = "BAUMBEISPIEL";
        int L = Beispiel.length();
        SuchBaum MeinBaum = new SuchBaum();

        for (int i = 0; i < L; i++){
            char c = Beispiel.charAt(i);
            System.out.print(c);
            Element e = new Element(new Character(c));
            MeinBaum.einfuegen(e);
            }
        System.out.println();
        
        MeinBaum.DumpBaum();

        if (MeinBaum.istLeer())System.out.println("Sch...! Der Baum blieb leer!");
        else {
            MeinBaum.ausgabeVorw();
            MeinBaum.ausgabeRueckw();
           
            Element such = new Element(new Character('B'));
            Knoten zsuch = MeinBaum.suche(such);
            System.out.println("--->> Löschen von: " + zsuch);
            if (zsuch != null){
                MeinBaum.DumpKnoten(zsuch);
                MeinBaum.loescheKnoten(zsuch);
                MeinBaum.ausgabeVorw();
                MeinBaum.ausgabeRueckw();
                MeinBaum.DumpBaum();
                }
            else System.out.println("Gesuchtes Element <" + such + "> nicht gefunden");
            
            }
        System.out.println("");
        }
      }
