This commit is contained in:
2023-05-17 12:40:09 +02:00
parent 45b4ed5d3e
commit c2d936cc1a
5 changed files with 709 additions and 0 deletions

409
AuD/src/UEB06/Baum.java Normal file
View File

@@ -0,0 +1,409 @@
package UEB06;
public class Baum<T extends Comparable<T>>
{
private Knoten<T> wurzel;
// Wird nur f<>r grafische Oberfl<66>che ben<65>tigt, ohne
// diese Methode k<>nnte die gesamte Implementierung
// des Baumes geheim gehalten werden. Alle <20>ffentlichen
// Methoden sind parameterlos oder besitzen als
// einzigen Parameter einen Schl<68>sselwert
public Knoten<T> getWurzel()
{
return wurzel;
}
public boolean istLeer()
{
return (wurzel == null);
}
public void attach(Knoten<T> einKnoten)
{
wurzel = einKnoten;
}
// Methoden zum Suchen
public boolean suchen(final T daten)
{
return istLeer() ? false : suchenKnoten(daten, wurzel);
// Effiziente Kurzform f<>r:
// if (istLeer()) { return false; } else { return suchenKnoten(daten, wurzel); }
}
private boolean suchenKnoten(final T daten, Knoten<T> teilbaum)
{
if (teilbaum == null)
return false;
// Vergleichs-Ergebnis zwischenspeichern, da compareTo()
// aufw<66>ndig sein kann, und das Ergebnis mehrfach ben<65>tigt
// wird
final int cmp = daten.compareTo(teilbaum.getDaten());
return (cmp == 0) ? true : suchenKnoten(daten, (cmp < 0) ? teilbaum.getKnotenLinks() : teilbaum.getKnotenRechts());
// Effiziente Kurzform f<>r:
// if (cmp == 0) { return true; }
// else if (cmp < 0) { return suchenKnoten(daten, teilbaum.getKnotenLinks()); }
// else { return suchenKnoten(daten, teilbaum.getKnotenRechts()); }
}
// Methoden zum Einf<6E>gen
public void einfuegen(final T daten)
{
if (istLeer())
{
// Sonderfall, analog zu verketteten Listen
wurzel = new Knoten<T>(daten, null, null);
}
else
{
einfuegenKnoten(daten, wurzel);
}
}
// Generiert einen neuen Knoten mit <20>bergebenen Daten und f<>gt
// ihn (die Suchbaumeigenschaft erhaltend) als Blatt in den
// Baum ein, sofern die Daten noch nicht vorhanden sind.
private void einfuegenKnoten(final T daten, Knoten<T> teilbaum)
{
// Vergleichs-Ergebnis zwischenspeichern, da compareTo()
// aufw<66>ndig sein kann, und das Ergebnis mehrfach ben<65>tigt
// wird
final int cmp = daten.compareTo(teilbaum.getDaten());
// Daten schon vorhanden?
// Falls ja: alles erledigt!
if (cmp == 0)
return;
if (cmp < 0)
{
// Einzuf<75>gende Daten sind KLEINER als Daten im aktuellen Knoten
// und m<>ssen daher im LINKEN Teilbaum eingef<65>gt werden
if (teilbaum.getKnotenLinks() == null)
{
// Es gibt keinen linken Teilbaum -> neuen Knoten erzeugen
teilbaum.setKnotenLinks(new Knoten<T>(daten, null, null));
}
else
{
// Es existiert ein linker Teilbaum -> rekursiv weiter
einfuegenKnoten(daten, teilbaum.getKnotenLinks());
}
}
else
{
// Einzuf<75>gende Daten sind GROESSER als Daten im aktuellen Knoten
// und m<>ssen daher im RECHTEN Teilbaum eingef<65>gt werden
if (teilbaum.getKnotenRechts() == null)
{
// Es gibt keinen rechten Teilbaum -> neuen Knoten erzeugen
teilbaum.setKnotenRechts(new Knoten<T>(daten, null, null));
}
else
{
// Es existiert ein rechter Teilbaum -> rekursiv weiter
einfuegenKnoten(daten, teilbaum.getKnotenRechts());
}
}
}
// Methoden zum Entfernen
public void entfernen(final T daten)
{
// Leerer Baum?
// Falls ja, gibt es nicht zu entfernen!
if (istLeer())
return;
// Vergleichs-Ergebnis zwischenspeichern, da compareTo()
// aufw<66>ndig sein kann, und das Ergebnis mehrfach ben<65>tigt
// wird
final int cmp = daten.compareTo(wurzel.getDaten());
if (cmp == 0)
{
// Der Wurzel-Knoten muss entfernt werden!
// Sonderfall, analog zu verketteten Listen
entfernenWurzel();
}
else
if (cmp < 0)
{
// Zu l<>schende Daten kleiner als Daten in Wurzel;
// im linken Teilbaum weitersuchen falls existent
if (wurzel.getKnotenLinks() != null)
entfernenKnoten(daten, wurzel, wurzel.getKnotenLinks(), true);
}
else
{
// Zu l<>schende Daten gr<67><72>er als Daten in Wurzel;
// im rechten Teilbaum weitersuchen falls existent
if (wurzel.getKnotenRechts() != null)
entfernenKnoten(daten, wurzel, wurzel.getKnotenRechts(), false);
}
}
private void entfernenWurzel()
{
if (wurzel.getKnotenLinks() == null)
{
// Wurzel hat h<>chstens einen rechten Nachfolger.
// Der wird zur neuen Wurzel!
wurzel = wurzel.getKnotenRechts();
}
else
if (wurzel.getKnotenRechts() == null)
{
// Wurzel hat h<>chstens einen linken Nachfolger.
// Der wird zur neuen Wurzel!
wurzel = wurzel.getKnotenLinks();
}
else
{
// Rechter und linker Teilbaum nicht leer; zwei Nachfolger.
// Wurzel durch gr<67><72>ten Knoten im linken Teilbaum ersetzen!
ersetzeKnoten(wurzel);
}
}
// Sofern <20>bergebene Daten im Teilbaum vorhanden sind, werden sie gel<65>scht.
// Elternknoten wird ben<65>tigt, da dessen rechter bzw. linker Nachfolger ggf. auf
// den rechten bzw. linken Nachfolger des zu l<>schenden Knotens umgesetzt werden muss
// Ist linkerTeilbaum == true, wurde der linke Nachfolger des Elternknotens <20>bergeben,
// sonst der rechte. Wird ben<65>tigt, um zu entscheiden, ob der linke oder rechte
// Nachfolger des Elternknotens ge<67>ndert werden muss.
private void entfernenKnoten(final T daten, Knoten<T> elternknoten, Knoten<T> teilbaum, final boolean linkerTeilbaum)
{
// Vergleichs-Ergebnis zwischenspeichern, da compareTo()
// aufw<66>ndig sein kann, und das Ergebnis mehrfach ben<65>tigt
// wird
final int cmp = daten.compareTo(teilbaum.getDaten());
if (cmp == 0)
{
// Der Knoten mit den zu l<>schenden Daten wurde gefunden
if (teilbaum.getKnotenLinks() == null)
{
// Zu l<>schender Knoten hat h<>chstens einen rechten Nachfolger.
// Auf diesen vom Elternknoten aus verweisen!
if (linkerTeilbaum)
{
elternknoten.setKnotenLinks(teilbaum.getKnotenRechts());
}
else
{
elternknoten.setKnotenRechts(teilbaum.getKnotenRechts());
}
}
else
if (teilbaum.getKnotenRechts() == null)
{
// Zu l<>schender Knoten hat h<>chstens einen linken Nachfolger.
// Auf diesen vom Elternknoten aus verweisen!
if (linkerTeilbaum)
{
elternknoten.setKnotenLinks(teilbaum.getKnotenLinks());
}
else
{
elternknoten.setKnotenRechts(teilbaum.getKnotenLinks());
}
}
else
{
// Rechter und linker Teilbaum nicht leer; zwei Nachfolger!
// Zu l<>schenden Knoten durch gr<67><72>ten Knoten im linken Teilbaum ersetzten
ersetzeKnoten(teilbaum);
}
}
else
if (cmp < 0)
{
// Zu l<>schende Daten kleiner als Daten im aktuellen Knoten;
// im linken Teilbaum weitersuchen falls existent
if (teilbaum.getKnotenLinks() != null)
entfernenKnoten(daten, teilbaum, teilbaum.getKnotenLinks(), true);
}
else
{
// Zu l<>schende Daten gr<67><72>er als Daten im aktuellen Knoten;
// im rechten Teilbaum weitersuchen falls existent
if (teilbaum.getKnotenRechts() != null)
entfernenKnoten(daten, teilbaum, teilbaum.getKnotenRechts(), false);
}
}
// Ersetzt zu l<>schenden Knoten durch gr<67><72>ten Knoten im linken Teilbaum,
// indem Daten des gr<67><72>ten Knotens in zu l<>schenden Knoten kopiert werden.
// Vom Elternknoten des gr<67><72>ten Knotens aus muss auf den linken Teilbaum
// des gr<67><72>ten Knotens verwiesen werden. Der rechten Teilbaum des gr<67><72>ten
// Knotens ist immer leer (Def. gr<67><72>ter Knoten)
private void ersetzeKnoten(Knoten<T> zuLoeschenderKnoten)
{
// Gr<47><72>ten Knoten suchen; dessen rechter Nachfolger ist null.
// Daher kann rechter Nachfolger des zu l<>schenden Knotens <20>bernommen werden.
Knoten<T> elternknoten = zuLoeschenderKnoten;
Knoten<T> teilbaum = zuLoeschenderKnoten.getKnotenLinks();
Knoten<T> groessterKnoten = teilbaum;
while (teilbaum.getKnotenRechts() != null)
{
elternknoten = teilbaum;
teilbaum = teilbaum.getKnotenRechts();
groessterKnoten = teilbaum;
}
// Daten des gr<67><72>ten Knotens werden in zu l<>schenden Knoten kopiert
zuLoeschenderKnoten.setDaten(groessterKnoten.getDaten());
if (elternknoten == zuLoeschenderKnoten) // Gr<47><72>ter Knoten ist Wurzel des linken Teilbaums des zu L<>schenden
{
// Zu l<>schender Knoten ist gleichzeitig Elternknoten des gr<67><72>ten Knotens
// Rechter Teilbaum des zu l<>schenden Knotens muss erhalten bleiben
// Linken Teilbaum des zu loeschenden Knotens auf linken Teilbaum des gr<67><72>ten Knotens setzen
// Das sind die einzigen Nachfolger des gr<67><72>ten Knotens, da dessen rechter Teilbaum ja
// immer leer ist.
zuLoeschenderKnoten.setKnotenLinks(groessterKnoten.getKnotenLinks());
}
else
{
// Rechten, freiwerdenden Teilbaum des Elternknotens des gr<67><72>ten Knotens
// auf linken Teilbaum des gr<67><72>ten Knotens setzen
// Das sind die einzigen Nachfolger des gr<67><72>ten Knotens, da dessen rechter Teilbaum ja
// immer leer ist.
elternknoten.setKnotenRechts(groessterKnoten.getKnotenLinks());
}
}
// Methoden zum Traversieren
// Pre-Order
public String traversierePreOrder()
{
return (wurzel != null) ? traversierePreOrder(wurzel) : "Der Baum ist leer.";
}
private String traversierePreOrder(final Knoten<T> einKnoten)
{
assert(einKnoten != null);
String nodes = "";
nodes += einKnoten.getDaten();
if(einKnoten.getKnotenLinks() != null){
nodes += traversierePreOrder(einKnoten.getKnotenLinks());
}
if(einKnoten.getKnotenRechts() != null){
nodes += traversierePreOrder(einKnoten.getKnotenRechts());
}
return nodes;
}
// In-Order
public String traversiereInOrder()
{
return (wurzel != null) ? traversiereInOrder(wurzel) : "Der Baum ist leer.";
}
private String traversiereInOrder(final Knoten<T> einKnoten)
{
String nodes = "";
assert(einKnoten != null);
if(einKnoten.getKnotenLinks() != null){
nodes += traversiereInOrder(einKnoten.getKnotenLinks());
}
nodes += einKnoten.getDaten();
if(einKnoten.getKnotenRechts() != null){
nodes += traversiereInOrder(einKnoten.getKnotenRechts());
}
return nodes;
}
// public String traversiereInOrder()
// {
// return (wurzel != null) ? traversiereInOrder(wurzel,"") : "Der Baum ist leer.";
// }
//
// private String traversiereInOrder(final Knoten<T> einKnoten, String nodes)
// {
// assert(einKnoten != null);
//
//
// if(einKnoten.getKnotenLinks() != null){
// nodes = traversiereInOrder(einKnoten.getKnotenLinks(),nodes);
// }
//
// nodes += einKnoten.getDaten();
//
// if(einKnoten.getKnotenRechts() != null){
// nodes = traversiereInOrder(einKnoten.getKnotenRechts(),nodes);
// }
//
// return nodes;
// }
// Post-Order
public String traversierePostOrder()
{
return (wurzel != null) ? traversierePostOrder(wurzel) : "Der Baum ist leer.";
}
private String traversierePostOrder(final Knoten<T> einKnoten)
{
assert(einKnoten != null);
String nodes = "";
if(einKnoten.getKnotenLinks() != null){
nodes += traversierePostOrder(einKnoten.getKnotenLinks());
}
if(einKnoten.getKnotenRechts() != null){
nodes += traversierePostOrder(einKnoten.getKnotenRechts());
}
nodes += einKnoten.getDaten();
return nodes;
}
// Methoden zur Bestimmung der H<>he
public int hoehe()
{
return hoeheRek(wurzel);
}
private int hoeheRek(final Knoten<T> einKnoten)
{
return -1;
}
}

View File

@@ -0,0 +1,41 @@
package UEB06;
import java.awt.*;
public class BaumAnsicht
{
private Baum<Character> einBaum;
public BaumAnsicht(Baum<Character> einBaum)
{
this.einBaum = einBaum;
}
public void ausgeben(int xLinks, int xRechts, int y, Graphics g)
{
g.setColor(new Color(240, 240, 240));
g.fillRect(xLinks, 100, xRechts, 190);
ausgebenTeilbaum(einBaum.getWurzel(), -1, -1, xLinks, xRechts, y, g);
}
public void ausgebenTeilbaum(Knoten teilbaum, int xParent, int yParent, int xLinks, int xRechts, int y, Graphics g)
{
if (teilbaum != null)
{
final int mitte = (xLinks + xRechts) / 2;
g.setColor(Color.black);
g.drawString(teilbaum.toString(), mitte - 2, y);
if ((xParent != -1) && (yParent != -1))
{
g.setColor(new Color(192, 192, 192));
g.drawLine(xParent, yParent, mitte, y-12);
}
ausgebenTeilbaum(teilbaum.getKnotenLinks(), mitte, y + 4, xLinks + 4, mitte - 4, y + 30, g);
ausgebenTeilbaum(teilbaum.getKnotenRechts(), mitte, y + 4, mitte + 4, xRechts + 4, y + 30, g);
}
}
}

200
AuD/src/UEB06/BaumGUI.java Normal file
View File

@@ -0,0 +1,200 @@
package UEB06;
import java.awt.*;
import java.applet.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class BaumGUI extends Frame
{
private Baum<Character> einBaum;
private BaumAnsicht eineBaumAnsicht;
private java.awt.Label zeichenFuehrungstext = new java.awt.Label();
private java.awt.TextField zeichenTextfeld = new java.awt.TextField();
private java.awt.Button suchenDruckknopf = new java.awt.Button();
private java.awt.Button einfuegenDruckknopf = new java.awt.Button();
private java.awt.Button entfernenDruckknopf = new java.awt.Button();
private java.awt.Button demoDruckknopf = new java.awt.Button();
private java.awt.Checkbox enthaltenKontrollkaestchen = new java.awt.Checkbox();
private java.awt.Label infoLabel = new java.awt.Label();
private java.awt.Button traversierenDruckknopf = new java.awt.Button();
private java.awt.TextArea ausgabeTextfeld = new java.awt.TextArea("",0,0,TextArea.SCROLLBARS_NONE);
public BaumGUI()
{
setLayout(null);
setSize(530,505);
zeichenFuehrungstext.setText("Zeichen:");
zeichenFuehrungstext.setAlignment(java.awt.Label.RIGHT);
add(zeichenFuehrungstext);
zeichenFuehrungstext.setBounds(12,32,48,24);
add(zeichenTextfeld);
zeichenTextfeld.setBounds(72,32,48,24);
suchenDruckknopf.setLabel("Suchen");
add(suchenDruckknopf);
suchenDruckknopf.setBackground(java.awt.Color.lightGray);
suchenDruckknopf.setBounds(132,32,84,24);
einfuegenDruckknopf.setLabel("Einf<EFBFBD>gen");
add(einfuegenDruckknopf);
einfuegenDruckknopf.setBackground(java.awt.Color.lightGray);
einfuegenDruckknopf.setBounds(228,32,84,24);
entfernenDruckknopf.setLabel("Entfernen");
add(entfernenDruckknopf);
entfernenDruckknopf.setBackground(java.awt.Color.lightGray);
entfernenDruckknopf.setBounds(324,32,84,24);
demoDruckknopf.setLabel("Demo-Baum");
add(demoDruckknopf);
demoDruckknopf.setBackground(java.awt.Color.lightGray);
demoDruckknopf.setBounds(418,32,84,24);
enthaltenKontrollkaestchen.setLabel("Im Baum enthalten");
enthaltenKontrollkaestchen.setEnabled(false);
add(enthaltenKontrollkaestchen);
enthaltenKontrollkaestchen.setBounds(132,68,132,24);
add(infoLabel);
infoLabel.setBounds(14,292,500,20);
traversierenDruckknopf.setLabel("Traversieren");
add(traversierenDruckknopf);
traversierenDruckknopf.setBackground(java.awt.Color.lightGray);
traversierenDruckknopf.setBounds(14,320,127,26);
add(ausgabeTextfeld);
ausgabeTextfeld.setBounds(14,350,500,144);
AktionsAbhoerer einAktionsAbhoerer = new AktionsAbhoerer();
suchenDruckknopf.addActionListener(einAktionsAbhoerer);
einfuegenDruckknopf.addActionListener(einAktionsAbhoerer);
entfernenDruckknopf.addActionListener(einAktionsAbhoerer);
demoDruckknopf.addActionListener(einAktionsAbhoerer);
TastaturAbhoerer einTastaturAbhoerer = new TastaturAbhoerer();
zeichenTextfeld.addKeyListener(einTastaturAbhoerer);
traversierenDruckknopf.addActionListener(einAktionsAbhoerer);
addWindowListener(
new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
setVisible(false);
dispose();
System.exit(0);
}
}
);
einBaum = new Baum<Character>();
eineBaumAnsicht = new BaumAnsicht(einBaum);
updateBaumInfo();
}
public void paint(Graphics g)
{
eineBaumAnsicht.ausgeben(15, 500, 120, g);
}
public void updateBaumInfo()
{
final int h = einBaum.hoehe();
infoLabel.setText("Der Baum hat eine H<>he von " + h + ".");
}
// Innere Klassen
class AktionsAbhoerer implements java.awt.event.ActionListener
{
public void actionPerformed(java.awt.event.ActionEvent event)
{
Object object = event.getSource();
if ((object == einfuegenDruckknopf) || (object == entfernenDruckknopf) || (object == suchenDruckknopf))
{
String text = zeichenTextfeld.getText();
if ((text != null) && (text.length() > 0))
{
char zeichen = text.charAt(0);
if (object == einfuegenDruckknopf)
{
einBaum.einfuegen(zeichen);
zeichenTextfeld.setText("");
repaint();
updateBaumInfo();
}
else
if (object == entfernenDruckknopf)
{
einBaum.entfernen(zeichen);
repaint();
updateBaumInfo();
}
else
if (object == suchenDruckknopf)
{
enthaltenKontrollkaestchen.setState(einBaum.suchen(zeichen));
}
}
}
else
if (object == traversierenDruckknopf)
{
ausgabeTextfeld.setText("Pre-Order:\n");
ausgabeTextfeld.append(einBaum.traversierePreOrder());
ausgabeTextfeld.append("\n\nIn-Order:\n");
ausgabeTextfeld.append(einBaum.traversiereInOrder());
ausgabeTextfeld.append("\n\nPost-Order:\n");
ausgabeTextfeld.append(einBaum.traversierePostOrder());
}
else
if (object == demoDruckknopf)
{
System.out.println("XXX");
einBaum.attach(
new Knoten<Character>('E',
new Knoten<Character>('D',
new Knoten<Character>('A',
null,
null),
null),
new Knoten<Character>('S',
new Knoten<Character>('O',
null,
new Knoten<Character>('P',
null,
new Knoten<Character>('R',
new Knoten<Character>('Q',
null,
null),
null))),
new Knoten<Character>('Z',
null,
null))));
repaint();
updateBaumInfo();
}
}
}
class TastaturAbhoerer extends java.awt.event.KeyAdapter
{
public void keyReleased(java.awt.event.KeyEvent event)
{
if (event.getSource() == zeichenTextfeld)
{
String text = zeichenTextfeld.getText();
if ((text != null) && (text.length() > 0))
{
char zeichen = text.charAt(0);
if (event.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER)
{
einBaum.einfuegen(zeichen);
zeichenTextfeld.setText("");
repaint();
}
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
package UEB06;
public class BaumTest
{
public static void main(String[] args)
{
new BaumGUI().setVisible(true);
}
}

50
AuD/src/UEB06/Knoten.java Normal file
View File

@@ -0,0 +1,50 @@
package UEB06;
public class Knoten<T>
{
private T daten;
private Knoten<T> teilbaumLinks;
private Knoten<T> teilbaumRechts;
public Knoten(T daten, Knoten<T> teilbaumLinks, Knoten<T> teilbaumRechts)
{
this.daten = daten;
this.teilbaumLinks = teilbaumLinks;
this.teilbaumRechts = teilbaumRechts;
}
public T getDaten()
{
return daten;
}
public Knoten<T> getKnotenLinks()
{
return teilbaumLinks;
}
public Knoten<T> getKnotenRechts()
{
return teilbaumRechts;
}
public void setDaten(T daten)
{
this.daten = daten;
}
public void setKnotenLinks(Knoten<T> teilbaumLinks)
{
this.teilbaumLinks = teilbaumLinks;
}
public void setKnotenRechts(Knoten<T> teilbaumRechts)
{
this.teilbaumRechts = teilbaumRechts;
}
public String toString()
{
return this.daten.toString();
}
}