Ich habe hier eine grafische Darstellung eines binären Suchbaumes versucht zu programmieren. Wenn der Baum eine Höhe(=Tiefe) von 4 hat soll eine Meldung ausgegeben werden, dass der Baum bereits die Höhe soundso hat, es kann nichts mehr eingefügt werden.
Es funktioniert auch alles ganz gut, nur wird beim Schließen des Fensters auch der bereits gezeichnete Baum überzeichnet, bzw. gelöscht, der soll aber bleiben, dafür soll es dann später noch einen eigenen Clear-Button geben.
Ich bin mir sicher, dass das etwas mit den panels zu tun hat, komme aber nicht wirklich weiter. Könnte mir da jemand vielleicht weiterhelfen? Danke im voraus.
das hier ist die Methode, die jedes Mal, wenn eingefügt wird, aufgerufen wird. Überschreitet der Baum eben eine bestimmte Höhe, so soll eine Nachricht ausgegeben werden, ohne dass der bereits gezeichnete Baum überzeichnet wird bzw. beim Schließen verschwindet.
Mittels des ActionListener bzw. des KeyListener werden die Ereignisse ausgelöst
Die main-Methode bzw. der Frame sind in dieser Datei BinBaumPanelRahmen.java
Um das Programm auch "testen" zu können hier auch noch die Suchbaum-Datei
Es funktioniert auch alles ganz gut, nur wird beim Schließen des Fensters auch der bereits gezeichnete Baum überzeichnet, bzw. gelöscht, der soll aber bleiben, dafür soll es dann später noch einen eigenen Clear-Button geben.
Ich bin mir sicher, dass das etwas mit den panels zu tun hat, komme aber nicht wirklich weiter. Könnte mir da jemand vielleicht weiterhelfen? Danke im voraus.
Code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class BinBaumPanel extends JPanel implements ActionListener{
private BinBaum_grafisch b;
private int[] feld = {4,3,7,8,6,3,2,1,0,9,10,5,12};
private JButton enter, loeschen;
private JLabel l1;
private JTextField t1;
private Dimension d = new Dimension(50,27);
private int s, x_akt, y_akt, e_akt, x_Pos, y_Pos;
private int x_Prev, y_Prev, versatz_s, versatz_l, max_hoehe;
private Font f = new Font("Verdana", Font.BOLD, 12);
JPanel p1, p2;
public BinBaumPanel(){
b = new BinBaum_grafisch();
x_akt = 200;
y_akt = 80;
e_akt = 0;
versatz_l = 5;
max_hoehe = 4;
l1 = new JLabel("Geben Sie eine ganze Zahl >=0 ein:", SwingConstants.RIGHT);
t1 = new JTextField("");
t1.setPreferredSize(d);
t1.addKeyListener(new MyKL());
enter = new JButton("EINGEBEN");
enter.addActionListener(this);
add(l1);
add(t1);
add(enter);
}
public void ausgeben(int[] f){
Graphics g = getGraphics();
f = feld;
//einfuegenFeld(f);
for (int i=0; i<feld.length; i++){
if (i==feld.length)break;
int vorg = b.findeVorgaenger(feld[i], feld[i]);
x_Pos = b.getX(feld[i], x_akt, e_akt);
x_Prev = b.getX(vorg, x_akt, e_akt);
y_Pos = b.getY(feld[i], y_akt, e_akt);
y_Prev = b.getY(vorg, y_akt,e_akt);
if (x_Pos < x_Prev){
versatz_s = 7;
}
else {
versatz_s = 0;
}
String s = String.valueOf(feld[i]);
g.drawString(s, x_Pos-versatz_s, y_Pos);
g.drawLine(x_Prev, y_Prev, x_Pos, y_Pos);
}
}
public void paintComponent (Graphics grafik)
{
super.paintComponents(grafik);
grafik.setColor(Color.lightGray);
// Horizontale Rasterlinien
for (int y=0; y<this.getHeight(); y+= 40){
grafik.drawLine(0,y,400,y);
}
// Vertikale Rasterlinien
for (int x=0; x<getHeight(); x+= 20){
grafik.drawLine(x,40,x,500);
}
}
/**
* Fügt die Schlüssel des Feldes vom Typ int[] in
* den Suchbaum ein;
* @param feld das Feld von Schlüssel;
*/
public void einfuegenFeld(int[] feld){
for (int i=0; i<feld.length; i++){
b.einfuegen(feld[i]);
}
}
public void einfuegenSchluessel(int schluessel){
int s = Integer.parseInt(t1.getText());
b.einfuegen(s);
}
das hier ist die Methode, die jedes Mal, wenn eingefügt wird, aufgerufen wird. Überschreitet der Baum eben eine bestimmte Höhe, so soll eine Nachricht ausgegeben werden, ohne dass der bereits gezeichnete Baum überzeichnet wird bzw. beim Schließen verschwindet.
Code:
public void zeichneSchluessel(int schluessel){
Graphics g = getGraphics();
int s = schluessel;
b.einfuegen(s);
int h = b.getHoehe();
System.out.println(h);
if(b.getHoehe()>max_hoehe){
String info = "<HTML><BODY>Der Baum hat bereits " +
"die Höhe " + max_hoehe
+ ".
Es können keine weiteren Schlüssel "
+ "eingefügt werden!</BODY></HTML>";
JPanel jp = new JPanel();
jp.setLocation(50, 50);
JOptionPane.showMessageDialog(jp, info);
return;
}
int vorg = b.findeVorgaenger(s, s);
x_Pos = b.getX(s, x_akt, e_akt);
x_Prev = b.getX(vorg, x_akt, e_akt);
y_Pos = b.getY(s, y_akt, e_akt);
y_Prev = b.getY(vorg, y_akt,e_akt);
FontMetrics fm = getFontMetrics(f);
String s_zahl = String.valueOf(s);
int s_breite = fm.stringWidth(s_zahl);
versatz_s = (int) Math.round(s_breite/2);
//Umwandeln des Schlüssels vom Typ int in
//einen String und Ausgabe der Zahl und der Linien
String zahl = String.valueOf(s);
if (h==1){
g.drawString(zahl, x_Pos-versatz_s, y_Pos+versatz_l);
}
else{
g.drawString(zahl, x_Pos-versatz_s, y_Pos+versatz_l);
if (x_Prev > x_Pos){
g.drawLine(x_Prev-versatz_l, y_Prev+versatz_l, x_Pos+versatz_l, y_Pos-versatz_l);
}
else{
g.drawLine(x_Prev+versatz_l, y_Prev+versatz_l, x_Pos-versatz_l, y_Pos-versatz_l);
}
}
}
Mittels des ActionListener bzw. des KeyListener werden die Ereignisse ausgelöst
Code:
/**
* Gibt den Ebenenabstand auf der y-Achse zurück
*/
public int getEbenenAbstand(){
return b.getEbenenAbstand();
}
public void actionPerformed(ActionEvent e){
Object source = e.getSource();
if (source == enter){
int h = b.getHoehe();
System.out.println(h);
if(b.getHoehe()>max_hoehe){
String info = "<HTML><BODY>Der Baum hat bereits " +
"die Höhe " + max_hoehe
+ ".
Es können keine weiteren Schlüssel "
+ "eingefügt werden!</BODY></HTML>";
JPanel jp = new JPanel();
JOptionPane.showMessageDialog(jp, info);
//repaint();
//new HelloSwingFrame();
return;
}
if(t1.getText().equals("")){return;}
else{
System.out.println("Enter gedrückt!");
zeichneSchluessel(Integer.parseInt(t1.getText()));
}
//ausgeben(feld);
}
}
class MyKL implements KeyListener{
public void keyReleased(KeyEvent e)
{
int code = e.getKeyCode();
if (code == e.VK_ENTER){
if (t1.getText().equals("")){return;}
if(b.getHoehe()>max_hoehe){
return;
}
else{
System.out.println("Enter gedrückt!");
zeichneSchluessel(Integer.parseInt(t1.getText()));
}
}
}
public void keyPressed(KeyEvent e){}
public void keyTyped(KeyEvent e){}
}
}
Die main-Methode bzw. der Frame sind in dieser Datei BinBaumPanelRahmen.java
Code:
import javax.swing.*;
import java.awt.*;
/**
* Panel zur Darstellung eines binären Suchbaums
*/
public class BinBaumPanelRahmen extends JFrame{
public BinBaumPanelRahmen(String titel)
{
setTitle(titel);
setSize(400, 500);
setLocationRelativeTo(null);
getContentPane();
BinBaumPanel binbp = new BinBaumPanel();
add(binbp);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
BinBaumPanelRahmen p = new BinBaumPanelRahmen("Binärer Suchbaum");
}
}
Um das Programm auch "testen" zu können hier auch noch die Suchbaum-Datei
Code:
/** Ein abgeschlossener (encapsulation) Suchbaum
* implementiert mit dem State- und Sigleton-Pattern.
*/
public class BinBaum_grafisch
{
/** Die Wurzel Knoten
* Ein Baum ist durch die Wurzel und seine Unterbäume (Kinder) definiert
* Für den leeren Baum ist die Wurzel durch den LeerenKnoten
* repräsenteirt.
*/
private AKnoten _wurzel;
/** Erzeugt leerer Baum */
public BinBaum_grafisch() {
_wurzel = LeererKnoten.signleton();
}
/** Ist der Schlüssel im Baum (Suche)
* @param k Schlüssel
*/
public boolean suchen(int k) {
return _wurzel.suchen(k);
}
/** Fügt Schlüssel ein
* @param k Schlüssel
*/
public void einfuegen(int k) {
_wurzel.einfuegen(this, k);
}
/** Entfernt Schlüsel
* @param k Schlüssel
*/
public void entferne(int k) {
_wurzel.entferne(this, k);
}
/** Ist Baum leer
* @return true false leer
*/
public boolean istLeer() {
return _wurzel.istLeer();
}
/** Gibt den Baum in inorder aus. */
public void printInOrder() {
_wurzel.printInOrder();
}
/** gibt die jeweilige y-Koordinate zurück */
public int getY(int s, int y_akt, int e_akt){
int y = y_akt;
int e = e_akt;
return _wurzel.getY(this, s, y, e);
}
/** gibt die jeweilige x-Koordinate zurück */
public int getX(int s, int x_akt, int e_akt){
int x = x_akt;
int e = e_akt;
return _wurzel.getX(this, s, x, e);
}
/** errechnet die aktuelle Höhe des übergebenen
* key
*/
public int getHoehe(){
return _wurzel.getHoehe(this);
}
/** Entfernt grösster Schlüssel im Baum
* wird für löschen(k) benötigt. Darf nur auf
* nichtleerem Baum aufgerufen werden.
* @return grösster Schlüssel im Baum
*/
private int entferneGroesster() {
return _wurzel.entferneGroesster(this);
}
/** Setzt Wurzel im Baum
* @k neue Wurzel
*/
private void setzeWurzel(AKnoten k) {
_wurzel = k;
}
/**
* Gibt die Schrittweite auf der y-Achse zurück
*/
public int getEbenenAbstand(){
return _wurzel.getEbenenAbstand();
}
/**
* Gibt den Schlüssel des Vorgaengers zurück
* @param s der Schlüssel, dessen Vorgänger gesucht wird
* @param vor Platzhalter für Rückgabe
* return den Vorgängerschlüssel
*/
public int findeVorgaenger(int s, int vor){
return _wurzel.findeVorgaenger(this, s, vor);
}
/** Abstrakter Knoten
* Suchbaum aggregiert diesen Knoten als Wurzel.
* Die Abstrakte Klasse wird für das State-Pattern
* verwendet.
* State-Pattern ermöglicht eine einfache
* funktionsbezogene Implementierung.
*/
static private abstract class AKnoten
{
/* Alle Operationen die auf den Knoten möglich sein sollen
müssen hier definiert werden.
*/
public abstract boolean suchen(int k);
public abstract void einfuegen(BinBaum_grafisch b, int i);
public abstract void entferne(BinBaum_grafisch b, int k);
public abstract boolean istLeer();
public abstract void printInOrder();
public abstract int entferneGroesster(BinBaum_grafisch b);
public abstract int getX (BinBaum_grafisch b, int s, int x, int e);
public abstract int getY (BinBaum_grafisch b, int s, int y, int e);
public abstract int getHoehe (BinBaum_grafisch b);
public abstract int getEbenenAbstand();
public abstract int findeVorgaenger(BinBaum_grafisch b, int s, int vor );
}
/** Leerer Knoten
* Diese Klasse repräsentiert den leeren Knoten.
* Singleton Klasse -> es gibt nur eine Instanz,
* alle leeren Bäume sind gleich.
* Implementiert alle Operationen auf dem leeren Baum.
*/
static private class LeererKnoten extends AKnoten
{
public void einfuegen(BinBaum_grafisch b, int i) {
b.setzeWurzel(new NichtLeererKnoten(i));
}
public boolean suchen(int k) {
return false;
}
public void entferne(BinBaum_grafisch b, int k) {
return;
}
public boolean istLeer() {
return true;
}
public void printInOrder() {
return;
}
/* nicht definiert auf leerem Knoten */
public int entferneGroesster(BinBaum_grafisch b) {
System.exit(0);
//throw new Exception("Methode nicht definiert.");
return 0;
}
public int getX(BinBaum_grafisch b, int s, int x_akt, int e_akt){
return 500;
}
public int getY(BinBaum_grafisch b, int s, int y_akt, int e_akt){
return 500;
}
public int getHoehe(BinBaum_grafisch b){
return 0;
}
public int getEbenenAbstand(){
return 0;
}
public int findeVorgaenger (BinBaum_grafisch b, int s, int vor){
return 0;
}
private static AKnoten _einmalig = null;
public static AKnoten signleton() {
if (_einmalig == null) {
_einmalig = new LeererKnoten();
}
return _einmalig;
}
}
/** NichtLeererKnoten
* Zustand eines nicht leeren Knoten. Beinhaltet den
* Schlüssel.
* Implementiert alle Operationen auf einem Knoten
*/
static private class NichtLeererKnoten extends AKnoten
{
// Felder
/** Der linke Teilbaum */
private BinBaum_grafisch _links;
/** Der rechte Teilbaum */
private BinBaum_grafisch _rechts;
/** Der Schlüssel */
private int _schluessel;
/** Der Schlüssel */
private int vorgaenger;
/** die x-Position */
private int x_Pos;
/** die y-Position */
private int y_Pos;
/** die aktuelle Höhe */
private int hoehe;
/** die aktuelle Ebene */
private int ebene;
/** der Abstand der Ebenen */
private int y_ebene;
// Konstruktor
public NichtLeererKnoten(int schluessel) {
_schluessel = schluessel;
_links = new BinBaum_grafisch();
_rechts = new BinBaum_grafisch();
vorgaenger = 0;
x_Pos = 200; // Startposition x;
y_Pos = 20; // Startposition y;
hoehe = 0; // Starthoehe;
ebene = 1; // Startebene;
y_ebene = 40; // Abstand der Ebenen;
}
// Operationen
public void einfuegen(BinBaum_grafisch b, int i) {
if (i < _schluessel) {
_links.einfuegen(i);
} else if (i > _schluessel){
_rechts.einfuegen(i);
} else { return; }
}
/**
* Gibt den Vorgänger des übergebenen Schlüssels zurück
* @param b der binäre Suchbaum
* @param i der Schlüssel, dessen Vorgänger gesucht wird
* @param v Platzhaltervariable für den Schlüssel des Vorgängers
* @return vorgaenger der Schlüssel des Vorgängers
*/
public int findeVorgaenger(BinBaum_grafisch b, int i, int vor) {
if (i == _schluessel){
vorgaenger = vor;
return vorgaenger;
}
if (i < _schluessel) {
vorgaenger = _schluessel;
return _links.findeVorgaenger(i, vorgaenger);
} else if (i > _schluessel){
vorgaenger = _schluessel;
return _rechts.findeVorgaenger(i, vorgaenger);
}
return vorgaenger;
}
public void entferne(BinBaum_grafisch b, int k) {
if (k < _schluessel) { _links.entferne(k); }
else if (k > _schluessel) { _rechts.entferne(k); }
else {
// Dieser Knoten muss entfernt werden
if (_links.istLeer()) {
b.setzeWurzel(_rechts._wurzel);
} else if (_rechts.istLeer()) {
b.setzeWurzel(_links._wurzel);
} else {
_schluessel = _links.entferneGroesster();
}
}
}
public boolean suchen(int k) {
if (k == _schluessel) {
//System.out.println("\nGefunden");
return true; }
if (k < _schluessel) { return _links.suchen(k); }
else { return _rechts.suchen(k); }
}
public boolean istLeer() {
return false;
}
public void printInOrder() {
_links.printInOrder();
System.out.print(_schluessel + " ");
_rechts.printInOrder();
}
public int entferneGroesster(BinBaum_grafisch b) {
if (_rechts.istLeer()) {
b.setzeWurzel(_links._wurzel);
return _schluessel;
} else {
return _links.entferneGroesster();
}
}
/**
* Gibt die Y-Koordinate des Punktes wieder, sofern der Punkt
* enthalten ist. Ist der Schlüssel gefunden, wird der
* entsprechende Wert zurückgegeben, andernfalls
* wird rekursiv in den jeweiligen Unterbäumen weitergesucht.
* Diese Methode ließe sich auch in die Einfüge-Methode integrieren
* doch aus Gründen der Übersichtlichkeit, sollen sie vorerst getrennt
* bleiben.
* @param b der BinBaum
* @param s der Schlüssel
* @param y_akt der aktuelle Wert von y
* @param e_akt die aktuelle Ebene
*/
public int getY(BinBaum_grafisch b, int s, int y_akt, int e_akt){
boolean such = suchen(s);
y_Pos = y_akt;
//prev_Y = y_prev;
ebene = e_akt;
if (such){
if (s == _schluessel) {
return y_Pos; }
if (s < _schluessel) {
y_Pos+= 40;//y_ebene;
return _links.getY(s, y_Pos, ebene); }
else {
y_Pos+= 40; //y_ebene;
return _rechts.getY(s, y_Pos, ebene); }
}
return y_Pos;
}
/**
* Gibt die jeweilige Schrittweite, die dieser Ebene
* entspricht, zurück
* @param e die Ebene, auf der sich der Knoten befindet
* @return weite, die Schrittweite, um die der Knoten
* versetzt werden muss.
*/
private int schrittWeiteBerechnen(int e){
e = ebene;
int weite=0;
switch(e){
case 1: weite=80;break;
case 2: weite=40;break;
case 3: weite=20;break;
case 4: weite=10;break;
}
return weite;
}
/**
* Gibt den Abstand der Ebenen zurück
* @return die Schrittweite auf der y-Achse;
*/
public int getEbenenAbstand(){
return y_ebene;
}
/**
* Ist der Schlüssel enthalten, so wird der entsprechende Wert
* der x-Koordinate zurückgegeben. Dabei ist besonders zu
* beachten, auf welcher Ebene gerade operiert wird, bzw.
* ob es sich um den linken oder rechten Teilbaum handelt,
* dementsprechend muss addiert bzw. subtrahiert werden.
* Diese Methode stützt sich ab auf die
* Methode schrittWeiteBerechnen(int e), die die jeweilige
* Schrittweite berechnet.
* @param b der BinBaum
* @param s der Schlüssel
* @param y_akt der aktuelle Wert von y
* @param e_akt die aktuelle Ebene
*/
public int getX(BinBaum_grafisch b, int s, int x_akt, int e_akt){
boolean such = suchen(s);
x_Pos = x_akt;
//prev_X = x_prev;
ebene = e_akt;
if (such){
if (s == _schluessel) {
return x_Pos; }
if (s < _schluessel) {
ebene+= 1;
int temp = schrittWeiteBerechnen(ebene);
x_Pos-= temp;
return _links.getX(s, x_Pos, ebene); }
else {
ebene+= 1;
int temp = schrittWeiteBerechnen(ebene);
x_Pos+= temp;
return _rechts.getX(s, x_Pos, ebene);
}
}
ebene+=1;
return x_Pos;
}
/**
* Gibt die Höhe des jeweiligen Baumknotens zurück.
* (entspricht der jeweiligen Ebene)
* @param b der BinBaum
* @param s der Schlüssel
* @param hoehe_akt die aktuelle Hoehe
*/
public int getHoehe(BinBaum_grafisch b){
int hoehe_links = 1 + _links.getHoehe();
int hoehe_rechts = 1 + _rechts.getHoehe();
return (hoehe_links>hoehe_rechts)? hoehe_links : hoehe_rechts;
}
}
}