Informatik/Jahrgangsstufe Q/001 Klausuren/Themensortiert/03

Aufgabe 1: Begründen oder widerlegen Sie die folgenden Aussagen:
(1) Jeder Knoten eines Binärbaums hat zwei nichtleere Teilbäume
(2) Beim Löschen eines Elements aus einem Binärbaum ändert sich die Höhe
dieses Baumes.
(3) Beim Einfügen eines Elements in einen Binärbaum kann die Höhe dieses
Baumes unverändert bleiben.
(4) Die Inorder-Ausgabe eines Suchbaums ist immer Sortiert.
(5) Ein Binärbaum der Höhe 4 besitzt höchstens 15 Knoten.
(6) Ein Binärbaum mit der Höhe 5 besitzt mindestens 7 Knoten.
Aufgabe 1: Bäume − Binärbaume – Suchbäume
Im Unterricht haben wir drei verschiedene Gruppen von Bäumen kennen
gelernt:
Gruppe A: Suchbäume
Gruppe B: Binärbäume
Gruppe C: Bäume
Gruppe D: keine Bäume (Graphen)
Diese stehen in einem rechts veranschaulichten Hierarchieverhältnis.
(D) Graphen
(C) Bäume
(B) Binärbäume
(A) Suchbäume
a) Ordnen Sie jede der nachfolgenden Strukturen einer dieser Gruppen zu und begründen Sie
kurz, warum die Struktur zur angegebenen Hierarchie gehört.
(1)
(2)
(3)
C
B
F
E
(4)
(5)
10
6
4
7
04.03.2002
19.09.2001
9
5
P
8
08.07.2001
25.05.2002
24.12.2001
01.10.2003
b) Zeichnen Sie den Graphen eines zu Anfang leeren Suchbaumes, in den nacheinander die
folgenden Elemente eingefügt werden (Eine Zeichnung reicht aus!):
3, 1, 7, 2, 12, 6, 4, 10, 8, 5, 11, 9
c) Geben Sie zum nachfolgenden Binärbaum die Preorder-, Inorder- und PostorderTraversierung an.
oder
Binärbäumen
Spass
von
Traversieren
das
riesen
macht
d) Löschen Sie nacheinander die folgenden Elemente aus dem rechts
abgebildeten Suchbaum.
9, 8, 7, 3, 6,
Halten Sie sich dabei exakt an das im Unterricht entwickelte
Verfahren SuchLöschen (Anlage I). Zeichnen Sie nach jeder
Löschaktion den Graphen des daraus resultierenden Baumes.
(Insgesamt sind also 5 Bäume zu zeichnen!)
einen
6
3
2
1
8
5
4
e) Entwickeln Sie eine Funktion, welche die Anzahl der Blätter eines Baumes berechnet.
Verwenden Sie wann immer möglich die Binbaum-Methoden (Anlage II).
function TBinBaum.Blattanzahl: integer;
7
10
9
11
Aufgabe 3: Zeichnen Sie den Graphen eines zu Anfang leeren Suchbaumes, in den
nacheinander die folgenden Elemente eingefügt werden (Eine Zeichnung reicht
aus!):
3, 1, 7, 2, 12, 6, 4, 10, 8, 5, 11, 9
Aufgabe 4: Geben Sie zum nachfolgenden Binärbaum die Preorder-, Inorder- und PostorderTraversierung an.
der
Freitag
ein
ist
guter
zum
Tag
Klausur
schreiben
Aufgabe 5: Löschen Sie nacheinander die folgenden Elemente aus dem rechts abgebildeten
Suchbaum.
6
9, 8, 7, 3, 6,
Halten Sie sich dabei exakt an das im Unterricht
3
entwickelte Verfahren SuchLöschen (Anlage I). Zeichnen
2
5
7
Sie nach jeder Löschaktion den Graphen des daraus
resultierenden Baumes. (Insgesamt sind also 5 Bäume zu
1
4
zeichnen!)
Aufgabe 6: Entwickeln Sie eine Funktion, welche die Anzahl der Knoten eines Baumes
zählt, die genau einen nichtleeren Teilbaum besitzen. Die Funktion angewendet
auf den Baum aus Aufgabe 5 würde als Ergebnis die Zahl 2 liefern, da nur die
Knoten 2 und 5 genau einen nichtleeren Teilbaum besitzen.
Verwenden Sie wann immer möglich die Binbaum-Methoden (Anlage II).
function TBinBaum.HalbbaumAnzahl: integer;
Aufgabe 7: Die Inhalte eines Suchbaums sollen in einen File geschrieben werden.
a) Begründen Sie, warum eine Methode SaveToFile, welche in der Klasse
TSuchbaum deklariert würde, nur Textdateien erzeugen könnte.
b) Begründen Sie, warum eine Methode LoadFromFile in der Klasse
TSuchbaum sogar unsinnig wäre.
8
10
9
11
Aufgabe 1:
Binärbäume − Suchbäume
Hinweis: Sämtliche Abbildungen finden Sie im Anhang I.
a) Begründen oder widerlegen Sie:
(1) Der Baum aus ABBILDUNG 1 gehört zur Gruppe der Suchbäume.
(2) Der Baum aus ABBILDUNG 2 ist kein Suchbaum.
(3) Ein Binärbaum der Höhe 3 hat höchstens sechs Elemente.
(4) Das Suchen in einem Suchbaum mit 7 Elementen benötigt höchstens drei
Vergleiche.
(5) Die InOrder Ausgabe des Baums aus ABBILDUNG 2 lautet:
Karl, Emil, Otto, Bert, Fritz, Josef, Paul, Doris, Gerd, Moritz
(6) Der Baum aus ABBILDUNG 3 könnte eine Datenstruktur für das Expertensystem
„Tiereraten“ sein.
(7) Beim Expertensystem „Tiereraten“ müssen die Baumelemente in PreOrderNotation in einem File abgelegt werden, damit die Rekonstruktion der
Wissensstruktur möglich ist.
b) Im Unterricht haben Sie Algorithmen kennen gelernt, mit Hilfe derer Elemente in
Suchbäume eingefügt und Elemente aus Suchbäumen gelöscht werden können.
(1) Fügen Sie in einen anfangs leeren Suchbaum nacheinander folgende Elemente ein.
30, 10, 20, 40, 50, 45, 35, 25, 15, 5
Eine Zeichnung genügt.
(2) Löschen Sie in dem so entstandenen Baum nacheinander die folgenden Elemente.
25, 50, 30
Zeichnen Sie nach jeder Löschoperation den resultierenden Suchbaum.
(3) Geben Sie zu Ihrem Suchbaum aus Teilaufgabe (1) die zugehörige PostOrderAusgabe an.
(4) Die PreOrderAusgabe eines Suchbaums lautet:
8, 4, 0, 2, 6, 14, 10, 12, 18, 16, 20
Zeichnen Sie den zugehörigen Suchbaum.
c) Gegeben sei die folgende Datenstruktur eines Binärbaums über dem Inhalt integer.
(1)
(2)
type TIntBinBaum = class(TBinBaum)
public
Wurzelinhalt: integer;
constructor Create(w: Integer);
function WurzelinhaltToString: string; override;
end;
Implementieren Sie die Methode WurzelinhaltToString.
Erweitern Sie die Klasse TIntBinBaum um eine Methode, welche das größte
Element des Baums bestimmt.
Achtung: Es handelt sich hier nicht um einen Suchbaum!
(3) Erweitern Sie nun die Klasse TIntBinBaum um eine Methode
procedure StrukturierteAusgabe(Memo: TMemo);
die den Baum „strukturiert“ auf einem Memofeld ausgibt. ABBILDUNG 4 zeigt, wie
dies gemeint ist.
Anlage I:
6
3
9
1
0
4
2
7
10
5
8
ABBILDUNG 1: SUCHBAUM ?
Karl
Emil
Bert
Otto
Fritz
Doris
Josef
Gerd
Paul
Moritz
ABBILDUNG 2: SUCHBAUM ?
Kann
sprechen?
Lebt im
Käfig?
Papagei
Kann
miauen?
Kann
bellen?
Katze
Hund
ABBILDUNG 3: WISSENSSTRUKTUR BEIM „TIERERATEN“ ?
ABBILDUNG 4: STRUKTURIERTE AUSGABE IM MEMO (UM 90° LINKS GEDREHT)
Aufgabe 1:
Binärbäume
Im Morsealphabet ist jeder Buchstabe durch eine Aneinanderreihung von Punkten (·) und
Strichen (-) codiert. Die Codierung der Buchstaben lautet wie folgt:
Morsealphabet
A ·N -·
B -·· C -·-· D -··· E ·
F ··-· G --· H ···· I ··
J ·--- K -·- L ·-·· M –O --- P ·--· Q --·- R ·-· S ··· T U ··- V ···- W ·-- X -··- Y -·-- Z --··
Eine Möglichkeit, den Morse-Code zu verwalten, ist die Speicherung der Codes in einem
Binärbaum. Der Morsepunkt bewirkt dabei eine Verzweigung nach
links, der Morsestrich eine Verzweigung nach rechts. In jedem
•
Knoten des Baumes steht der Buchstabe, der durch den im Baum
vorausgegangenen Punkt-Strich-Code repräsentiert wird.
...
a) Stelle den Morsecode wie oben beschrieben als Baum dar. Der
−
Pfad zum Buchstaben R müsste in deinem Baum wie rechts
abgebildet aussehen.
...
Solltest du keinen Baum erstellen können, so erhältst du diese
•
Lösung von dem Aufsicht führenden Lehrer.
R
b) Übersetze mit Hilfe des in a) entwickelten Baumes den folgenden
Morsetext. Die |-Zeichen trennen die einzelnen Buchstaben.
-|---|·-··|·-··|·|-·-|·-··|·-|··-|···|··-|·-·
c)
Beschreibe, wie man algorithmisch vorgeht, wenn man einen Text mit Hilfe des Baumes
übersetzen möchte. Es ist kein exakter Algorithmus gefordert!
d) Wieso ist eine Trennung der einzelnen Buchstaben (hier durch ein |-Zeichen)
erforderlich? Begründe mit Hilfe des Baums aus Aufgabenteil a).
Für die Umsetzung dieser Speichermöglichkeit in DELPHI bietet sich das folgende
interface an:
uses BinBaum;
type TMorsebaum = class(TBinBaum)
public
Inhalt: char;
constructor Create(c: char);
function WurzelinhaltToString: string; override;
function Einlesen: TMorsebaum;
function MorsetextToKlartext(Morse: string): char;
function KlartextToMorsetext(Klar: char): string;
end;
e)
Implementiere die folgenden Methoden dieses Datentyps:
(i) constructor Create(c: char);
Erzeugt ein Blatt dessen Knoteninhalt der Buchstabe c ist.
(ii) function WurzelinhaltToString: string; override;
Liefert den Knoteninhalt als string.
(iii) function MorsetextToKlartext(Morse: string): char;
Gibt zu einem Zeichen in Morsecode (Morse) den entsprechenden Buchstaben
im Klartext zurück.
MorsetextToKlartext(´··-·´) liefert den Buchstaben ´F´.
(iv) ... siehe nächste Seite ...
...
(iv) function KlartextToMorsetext(Klar: char): string;
Sucht im Baum den Buchstaben Klar und gibt den entsprechenden Morsetext
zurück.
KlartextToMorsetext(´V´) liefert den Morsecode ´···−´.
Hinweis: Am besten ist diese Aufgabe zu lösen, wenn du dir eine
Hilfsfunktion/-prozedur schreibst, welche rekursiv alle möglichen
Morsecodes durchläuft.
f)
Eine andere Möglichkeit der Verwaltung der Morsecodes wäre durch ein Array gegeben:
const CodeTabelle: array['A'..'Z'] of string[4]=
('·-','-··','-·-·','-···','·','··-·',
'--·','····','··','·---','-·-','·-··',
'--','-·','---','·--·','--·-','·-·',
'···','-','··-','···-','·--','-··-','-·--','--··');
Welche Nachteile hat diese Art der Verwaltung gegenüber der Verwaltung in Form eines
Baums? Welche Vorteile ergeben sich aus dieser Art der Speicherung?
Aufgabe 3: Aus dem Unterricht kennen Sie den Datentyp des Binärbaums (Anlage I).
a) Beschreiben Sie die wesentlichen Merkmale dieser Datenstruktur.
b)
beißt?
bellt?
Hund
miaut?
Löwe
Katze
Fisch
Der Datentyp wird um folgende Methode erweitert:
function TBinBaum.wasBerechneIch: string;
begin
if leer
then result:= ´´
else result:= LinkerTeilbaum.wasBerechneIch +
WurzelinhaltToString +
RechterTeilbaum.wasBerechneIch;
end;
Welches Funktionsergebnis wird für den links abgebildeten Baum
zurückgegeben?
c) Schreiben Sie eine Methode für den Datentyp TBinBaum, welche die Anzahl
der Tiere in einer Wissensstruktur berechnet. Hinweis: In den Blättern eines
Wissensbaums standen stets Tiernamen.
d) Erläutern und formulieren Sie einen Algorithmus, welcher die Inhalte eines
Binärbaums zeilenweise ausgibt. Eine exakte Implementierung ist hier nicht
gefordert. Hinweis: Sie dürfen auf bereits bekannte Datenstrukturen (Lineare
Liste, Stapel, Schlange, etc.) zurückgreifen.
Aufgabe 1: Binärbäume − hier: Termbäume
In der fünften Klasse lernt man, nach welchen Rechengesetzen Terme berechnet werden:
Klammer- vor Punkt- und Punkt- vor Strichrechnung. Zur Veranschaulichung werden
sogenannte Rechenbäume oder Termbäume benutzt, welche die Reihenfolge der Berechnung
veranschaulichen. Zwei Beispiele:
2+3*6−4/1
Beispiel 1:
5 * (6 + 2) − 7 / 4 + 2 * 5
Beispiel 2:
+
–
/
+
2
*
3
*
−
4
1
6
/
*
5
+
6
2
7
5
4
2
a) Stelle die zugehörigen Terme auf:
*
/
+
2
+
–
7
/
5
*
5
5
+
4
3
2
1
2
b) Stelle die zugehörigen Termbäume zu den Termen (I) 7 * 6 − (3 * 5 − 2 + 1) und
(II) 3 − 8 / 4 * 3 + 5 auf.
c)
Durchläuft man einen Termbaum in Preorder, so erhält man den sogenannten PräfixTerm, durchläuft man ihn in Postorder, so erhält man den Postfix-Term.
Gib sowohl den Präfix-Term als auch den Postfix-Term der Beispiel-Termbäume an
(Beispiel 1 und 2).
d) Gegeben ist nun der Präfix-Term (I) – * 2 + 2 3 + 6 1. Stelle den zugehörigen Termbaum
auf. Versuche das gleiche mit den Präfix-Termen (II) * + 8 7 − 7 / 4 2 und
(III) + + + 1 1 1 + + 1 1 + 1 . Begründe bei (III) dein Ergebnis.
e)
Gegeben ist nun der Postfix-Term (I) 1 3 5 * + 6 − 8 3 * −. Stelle den zugehörigen
Termbaum auf. Versuche das gleiche mit den Postfix-Termen (II) 8 7 7 * + 4 2 / − und
(III) 1 * + 3 4 – *. Begründe bei (III) dein Ergebnis.
In der Anlage (Anlage I) findest du eine mögliche Klassendefinition TTermbaum für einen
Termbaum. Dieser ist abgeleitet vom Datentyp Binärbaum und erbt somit alle in TBinbaum
definierten Methoden.
f)
Schreibe die Methode ToPostorder der Klasse TTermbaum, welche den Termbaum
als Postfix-Term zurückgibt.
g)
Beschreibe algorithmisch, wie die Funktion FromPreorder aus einem Präfix-Term
den zugehörigen Termbaum erstellen kann. „Befehle“ wie „Lies nächstes Zeichen“,
„Prüfe, ob Zeichen ein Operator ist“, etc. sind dabei in Ordnung.
unit TermBaum;
interface
uses BinBaum
const Operatoren: set of char= ['+','-','*','/','^'];
Operanden: set of char= ['0'..'9'];
{ Menge der Operatoren
{ Menge der Operanden
}
}
type TTermbaum = class(TBinBaum)
Inhalt: char;
constructor create(z: char);
function WurzelinhaltToString: string; override;
function ToPreOrder: string;
function ToInOrder: string;
function ToPostOrder: string;
function Auswertung: Integer;
end;
{
{
{
{
{
{
{
}
}
}
}
}
}
}
nur Ziffern als Operanden erlaubt
Konstruktor mit Übergabe des Wurzelinhalts
Aus TBinbaum geerbt
Gibt den Termbaum als PreOrder-String aus
Gibt den Termbaum als InOrder-String aus
Gibt den Termbaum als PostOrder-String aus
Wertet den Termbaum arithmetisch aus
function FromPreOrder(var Zeichenkette: string): TTermBaum; { Erzeugung eines Termbaums aus PreOrder-String
function FromInOrder(var Zeichenkette: string): TTermbaum;
{ Erzeugung eines Termbaums aus InOrder-String
function FromPostOrder(var Zeichenkette: string): TTermbaum; { Erzeugung eines Termbaums aus PostOrder-String
Eine mögliche Oberfläche zur Darstellung von Termbäumen ist die folgende:
}
}
}
Binärbäume
Aufgabe 1:
In der fünften Klasse lernt man, nach welchen Rechengesetzen Terme berechnet werden:
Klammer- vor Punkt- und Punkt- vor Strichrechnung. Zur Veranschaulichung werden
sogenannte Rechenbäume oder Termbäume benutzt, welche die Reihenfolge der Berechnung
veranschaulichen. Zwei Beispiele:
2+3*6−4/1
Beispiel 1:
5 * (6 + 2) − 7 / 4 + 2 * 5
Beispiel 2:
–
+
+
/
2
*
3
*
−
4
1
6
*
5
/
+
6
2
7
5
4
2
a) Gib für folgende Termbäume den zugehörigen Term an.
*
/
+
2
–
7
/
5
+
*
5
5
+
4
3
2
2
b) Zeichne die Termbäume zu den Termen
(I) 7 * 6 − (3 * 5 − 2 + 1) und
(II) 3 − 8 / 4 * 3 + 5.
c)
Durchläuft man einen Termbaum in Preorder, so erhält man den sogenannten PräfixTerm, durchläuft man ihn in Postorder, so erhält man den Postfix-Term.
Gib sowohl den Präfix-Term als auch den Postfix-Term der Beispiel-Termbäume an
(Beispiel 1 und 2).
d) Gegeben sind nun der Präfix-Terme
(I) – * 2 + 2 3 + 6 1
(II) * + 8 7 − 7 / 4 2 und
(III) + + + 1 1 1 + + 1 1 + 1 1
Stelle die Terme als Termbaum dar.
e)
Gegeben sind nun die Postfix-Terme
(I) 1 3 5 * + 6 − 8 3 * −
(II) 8 7 7 * + 4 2 / − und
(III) 1 1 + 1 + 1 1 + 1 + +
Stelle die Terme als Termbaum dar.
1
f) Die Datenstruktur eines Termbaums ist durch folgendes UML-Diagramm gegeben.
I) Erläutere die Beziehungen dieser Datenstruktur.
II) Begründe, warum eine Vererbung der Klasse TTermbaum von der Klasse TBinTree
nicht sinnvoll ist.
g) Implementiere die private Methode BinTreeToPostOrder, welche die zu dem
übergebenen Binärbaum zugehörige Postfixterm-Darstellung zurückliefert.
h) Vervollständige das folgende Raster der privaten Methode PreOrderToBinTree, welche
aus einem Präfixterm einen Binärbaum erstellt und zurückgibt.
Hinweise: Eine Fehlerprüfung, ob tatsächlich ein Präfixterm vorliegt, ist nicht erforderlich.
Alle Operanden sind Ziffern. Leerzeichen wurden zuvor aus der Zeichenkette entfernt.
const Operatoren: set of char= ['+','-','*','/','^'];
Operanden: set of char= ['0'..'9'];
function NaechstesZeichen(var Zeichenkette: string): char;
begin
result:= Zeichenkette[1];
Delete(Zeichenkette,1,1);
end;
function TTermbaum.PreOrderToBinTree(var Zeichenkette: string): TBinTree;
var ltb, rtb: TBinTree;
Zeichen: char;
begin
Zeichen:= NaechstesZeichen(Zeichenkette);
if Zeichen in Operatoren // prüfe, ob das Zeichen ein Operator ist.
then begin
// hier bitte den richtigen Code ergänzen.
end
else result:= TBinTree.Create(TOperandObjekt.create(StrToInt(Zeichen)));
end;
Materialien
Die Klasse TBinTree
In einem Objekt der Klasse TBinTree werden beliebige Objekte in einem Binärbaum verwaltet. Ein
Binärbaum ist entweder leer oder besteht aus einem Knoten, dem ein Element und zwei binäre
Teilbäume, die so genannten linken und rechten Teilbäume, zugeordnet sind.
Dokumentation der Methoden der Klasse TBinTree
Konstruktor create
nachher
Ein leerer Baum existiert
Konstruktor create (pObject: TObject)
nachher Der Binärbaum existiert und hat einen Wurzelknoten mit dem Inhalt pObject und
zwei leeren Teilbäumen.
Konstruktor create (pObject: TObject; pLeftTree, pRightTree: TBinTree)
nachher Der Binärbaum existiert hat einen Wurzelknoten mit dem Inhalt pObject, dem
linken Teilbaum pLeftTree und dem rechten Teilbaum pRightTree.
Anfrage
nachher
Auftrag
nachher
isEmpty: boolean
Diese Anfrage liefert den Wahrheitswert true, wenn der Binärbaum leer ist, sonst
liefert sie den Wert false.
clear
Der Binärbaum ist leer. Auch die Inhaltsobjekte der Knoten wurden freigegeben
und stehen nicht mehr zur Verfügung.
Auftrag
nachher
setRootItem (pObject: TObject)
Die Wurzel hat – unabhängig davon, ob der Binärbaum leer ist oder schon eine
Wurzel hat – pObject als Inhalt. Eventuell vorhandene Teilbäume werden nicht
geändert.
Anfrage
vorher
nachher
getRootItem: TObject
Der Binärbaum ist nicht leer.
Diese Anfrage liefert den Inhalt des Wurzelknotens des Binärbaums.
Auftrag
vorher
nachher
setLeftTree (pTree: TBinTree)
Der Binärbaum ist nicht leer.
Die Wurzel hat den übergebenen Baum als linken Teilbaum.
Auftrag
vorher
nachher
setRightTree (pTree: TBinTree)
Der Binärbaum ist nicht leer.
Die Wurzel hat den übergebenen Baum als rechten Teilbaum.
Anfrage
vorher
nachher
getLeftTree: TBinTree
Der Binärbaum ist nicht leer
Diese Anfrage liefert den linken Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Anfrage
vorher
nachher
getRightTree: TBinTree
Der Binärbaum ist nicht leer
Diese Anfrage liefert den rechten Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Auftrag
nachher
destroy
Der Binärbaum existiert nicht mehr.
Aufgabe 1:
Bäume
Im Unterricht haben Sie Datenstrukturen für Binärbäume (BinTree) und geordnete
Binärbäume (OrderedTree) kennen gelernt. Beide Datenstrukturen hatten den Nachteil,
dass jeder Knoten lediglich zwei Sohnknoten besaß. Es gibt allerdings Anwendungsbeispiele,
bei denen es hilfreich wäre, dass jeder Knoten beliebig viele Sohnknoten speichert. Beispiele
hierfür sind Entscheidungsbäume bei Strategiespielen (eine Spielsituation hat eine
unterschiedliche Anzahl von Folge-Situationen) oder Ahnenbäume (ein Mensch hat nicht
immer genau zwei Nachkommen).
Im Folgenden sollen Sie Teile einer hierfür geeigneten Datenstruktur Tree modellieren und
implementieren.
a) Die Speicherung der Sohnknoten eines Trees soll mittels einer List (siehe Anlage)
realisiert werden. Folgende Konstruktoren, Aufträge und Anfragen sollen in der Klasse
Tree Berücksichtigung finden:
(1) zwei Konstruktoren zur Erzeugung eines leeren Trees oder eines Trees mit einem
Inhalt vom Typ Object.
(2) eine Anfrage, ob der Tree leer ist
(3) einen Auftrag zum Leeren des Trees, d. h. alle Inhalte werden gelöscht
(4) eine Anfrage nach dem Inhalt der Wurzel des Trees
(5) einen Auftrag zum Setzen des Inhalts der Wurzel des Trees
(6) eine Anfrage nach einem Teilbaum des Trees, wobei durch eine Nummer bestimmt
wird, der wievielte Teilbaum angefragt wird.
(7) Auftrag zum Anhängen eines neuen Teilbaums an den Tree. Die Teilbäume
benötigen keine spezielle Ordnung.
Zeichnen Sie ein UML-Klassendiagramm der Klasse Tree mit allen Attributen und
Methoden.
Ergänzen Sie das Diagramm um ein Klassendiagramm der Klasse List (ohne Methoden
und Attribute) und setzen Sie beide Klassen sinnvoll in Beziehung zueinander.
b) Dokumentieren Sie die Anfrage nach einem Teilbaum (6) sowie den Auftrag zum Setzen
des Inhalts der Wurzel eines Trees (5) ausführlich mit vorher/nachher-Bedingung. Gehen
Sie auch auf Spezialfälle ein.
c) Implementieren Sie beide Konstruktoren der Klasse Tree (1) sowie die Methode für die
Anfrage nach einem Teilbaum (6). Achten Sie auf die in b) dokumentierten Spezialfälle.
d) Implementieren Sie die Methode
String preOrder(Tree pTree)
welche die Wurzelinhalte des Baums pTree in preorder-Traversierung
semikolongetrennt in einem String ausgibt.
Hinweis: Die Klasse Object verfügt über eine Methode toString(), welche zur
Konvertierung in einen String genutzt werden kann.
Aufgabe 1: AVL-Bäume
Aus dem Unterricht kennen Sie die Datenstruktur TAVLBaum. (siehe Interface in Anlage I)
a) Geben Sie die Definition für einen AVL-Baum an. Erläutern Sie den wesentlichen Vorteil
im Gegensatz zu den ihnen bekannten Baumstrukturen. Erläutern Sie wenigstens zwei
weitere Möglichkeiten der Baumoptimierung.
b) Fügen Sie nacheinander die folgenden Elemente in einen anfangs leeren AVL-Baum ein.
Zeichnen Sie nach jeder AVL-Korrekturoperation (Links-/Rechtsrotation) den Baum neu.
3, 4, 7, 1, 2, 6 und 5
8
c) Löschen Sie im rechts abgebildeten AVL-Baum das
Element mit der Nr. 8. Stellen Sie anschließend die
AVL-Eigenschaft wieder her.
5
11
2
1
7
3
9
6
14
10
13
4
d) Das Einfügen und Löschen in AVL-Bäumen kann zur
Störungen der AVL-Balance führen. Skizzieren Sie die um Unterricht entwickelte
Fallunterscheidung, welche alle möglichen Situationen vor und nach den
Korrekturoperationen enthält.
12
X h+1
e) Implementieren Sie den Teil der Methode
function TAVLBaum.AVLKorrektur: TAVLBaum.
der für einen Baum in rechts abgebildeter Situation die AVLEigenschaft wieder herstellt.
if (Balance = −2) and (LinkerTeilbaum.Balance = −1) then ...
Y h
h−1
h−2
f) Die von TAVLBaum abgeleitete Klasse TIntAVLBaum soll
Integer-Werte aufnehmen können. Implementieren Sie für diese Klasse eine Methode
function TIntAVLBaum.AVL_PerfekterAusgleich: TAVLBaum.
Die Funktion soll den Baum InOrder auf einen File abgelegen und den dann per
Halbteilungsmethode eingelesen Baum zurückgeben.
Verwenden Sie wann immer möglich die Methoden des AVL-Baumes bzw. die des ihnen
bekannten Binärbaumes.
5
7
3
1
4
6
2
File:
1
2
3
4
5
6
7
4
2
1
6
3
5
7
EOF
h−2
15
Aufgabe 2:
AVL-Bäume
a) Bauen Sie schrittweise einen AVL-Baum auf. Zeichnen Sie den Baum nach jeder
Korrekturoperation:
10, 20, 30, 110, 100, 90, 80, 70, 60, 50, 40
b) Löschen Sie nun nacheinander die folgenden Elemente. Zeichne Sie nach jedem
Löschvorgang den aktuellen Baum:
50, 40, 30, 20
c) Geben Sie die Prorder- und Postorder-Ausgabe des Baums aus Teilaufgabe a) an.
d) Schreiben Sie eine Funktion, welche einen vorhandenen Baum in PROLOGNotation ausgibt.
function TAVLBaum.PrologAusgabe: string;
begin
{ hier kommt der Programmtext hin... }
end;
30
10
50
20
40
z. B. sollte der rechts abgebildete Baum die folgende Rückgabe bewirken:
baum(30,baum(10,nil,baum(20,nil,nil)),baum(50,baum(40,nil,nil),baum(60,nil,nil)))
e) Begründen Sie mit Hilfe eines geeigneten Beispiels: Das Löschen eines Elements in einem
AVL-Baum kann auf mehreren Ebenen Korrekturoperationen nach sich ziehen.
f) Ist es möglich, dass nach dem Einfügen eines neuen Elements in einen AVL-Baum die
AVL-Balance eines Knotens −3 beträgt? Begründen Sie!
g) Die Ausgewogenheit eines nichtleeren AVL-Baumes ist definiert durch das Verhältnis
Summe der absoluten Balancen
a=
. Dabei meint man mit absoluten Balancen die
Anzahl der Knoten
jeweiligen Beträge der Balancen.
Begründen Sie die untere Schranke: a ≥ 0. Zeigen Sie durch ein Beispiel, dass a = 0
möglich ist.
Geben Sie eine möglichst kleine obere Schranke für diese Maßzahl a an. Begründen Sie
Ihre Wahl anhand geeigneter Beispiele.
Lösung:
Aufgabe 2:
a)
b)
c)
d)
function TAVLBaum.PrologAusgabe: string;
begin
if BaumLeer
then Result:= ´nil´
else Result:= ´baum(´+WurzelinhaltToString+´,´+
+(linkerTeilbaum as TAVLBaum).PrologAusgabe
+´,´
+(rechterTeilbaum as TAVLBaum).PrologAusgabe
+´)´;
end;
60
Aufgabe 2:
Bäume – Binärbäume – Suchbäume
a) Im Unterricht haben wir verschiedene Gruppen von Bäumen kennen
gelernt:
Gruppe A: AVL-Bäume
Gruppe B: Suchbäume
Gruppe C: Binärbäume
Gruppe D: Bäume
Gruppe E: keine Bäume (Graphen)
Diese stehen in einem rechts veranschaulichten Hierarchieverhältnis.
(E) Graphen
(D) Bäume
(C) Binärbäume
(B) Suchbäume
(A) AVL-Bäume
Ordnen Sie jede der nachfolgenden Strukturen in die Gruppenhierarchie ein. Begründen
Sie kurz, warum die Struktur zur angegebenen Hierarchiestufe gehört.
(1)
(2)
0
(3)
–6
6
3
9
1
(4)
010
001
(5)
100
(6)
110
011 101
K
F
111
T
L
N
b) Das Einfügen und Löschen in AVL-Bäumen kann dazu führen, dass die AVL-Eigenschaft
verloren geht, welche dann durch Rotationen wieder hergestellt werden muss.
Stellen Sie dar, wie die folgenden Elemente nacheinander in einen
anfangs leeren AVL-Baum eingefügt werden. Ihre Darstellung muss
auch die AVL-Korrekturoperationen beinhalten.
7, 6, 1, 4, 2, 5, 3
Stellen Sie das schrittweise Löschen der Elemente 7, 10, 3, 6 im rechts
abgebildeten AVL-Baum dar.
6
3
2
8
4
1
7
5
c) Implementieren Sie eine Methode für den allgemeinen Binärbaum, welche überprüft, ob
die AVL-Eigenschaft erfüllt ist. Verwenden Sie wann immer möglich die Methoden der
Klasse TBinTree (Anlage I). Eventuelle Hilfsmethoden sind ebenfalls zu implementieren.
function TBinTree.AVL_ok: boolean;
d) Die Daten eines Integer-Suchbaumes sollen in einem File abgelegt werden. Die
gespeicherten Daten sollen so gespeichert sein, dass eine identische Rekonstruktion der
alten Baumstruktur möglich ist.
Erläutern Sie eine Idee, wie die Daten gespeichert werden müssten. Begründen Sie dabei,
dass über die eigentlichen Daten hinaus keine weitere Informationen abgespeichert
werden müssen, um eine identische Rekonstruktion zu ermöglichen.
Entwickeln Sie einen Grobalgorithmus, der die Rekonstruktion des Baumes erzeugt.
Die Klasse TBinTree
In einem Objekt der Klasse TBinTree werden beliebige Objekte in einem Binärbaum
verwaltet. Ein Binärbaum ist entweder leer oder besteht aus einem Knoten, dem ein
Element und zwei binäre Teilbäume, die so genannten linken und rechten
Teilbäume,
10
9
11
zugeordnet sind.
Dokumentation der Methoden der Klasse TBinTree
Konstruktor create
nachher
Ein leerer Baum existiert
Konstruktor create (pObject: TObject)
nachher Der Binärbaum existiert und hat einen Wurzelknoten mit dem Inhalt pObject und
zwei leeren Teilbäumen.
Konstruktor create (pObject: TObject; pLeftTree, pRightTree: TBinTree)
nachher Der Binärbaum existiert hat einen Wurzelknoten mit dem Inhalt pObject, dem
linken Teilbaum pLeftTree und dem rechten Teilbaum pRightTree.
Anfrage
nachher
Auftrag
nachher
isEmpty: boolean
Diese Anfrage liefert den Wahrheitswert true, wenn der Binärbaum leer ist, sonst
liefert sie den Wert false.
clear
Der Binärbaum ist leer. Auch die Inhaltsobjekte der Knoten wurden freigegeben
und stehen nicht mehr zur Verfügung.
Auftrag
nachher
setRootItem (pObject: TObject)
Die Wurzel hat – unabhängig davon, ob der Binärbaum leer ist oder schon eine
Wurzel hat – pObject als Inhalt. Eventuell vorhandene Teilbäume werden nicht
geändert.
Anfrage
vorher
nachher
getRootItem: TObject
Der Binärbaum ist nicht leer.
Diese Anfrage liefert den Inhalt des Wurzelknotens des Binärbaums.
Auftrag
vorher
nachher
addTreeLeft (pTree: TBinTree)
Der Binärbaum ist nicht leer.
Die Wurzel hat den übergebenen Baum als linken Teilbaum.
Auftrag
vorher
nachher
addTreeRight (pTree: TBinTree)
Der Binärbaum ist nicht leer.
Die Wurzel hat den übergebenen Baum als rechten Teilbaum.
Anfrage
vorher
nachher
getLeftTree: TBinTree
Der Binärbaum ist nicht leer
Diese Anfrage liefert den linken Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Anfrage
vorher
nachher
getRightTree: TBinTree
Der Binärbaum ist nicht leer
Diese Anfrage liefert den rechten Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Auftrag
nachher
destroy
Der Binärbaum existiert nicht mehr.
Lösung:
Aufgabe 2:
a) Die Zuordnung sieht wie folgt aus:
(1): Gruppe E. Nicht D, da es zu einem Element unterschiedliche Wege gibt.
(2): Gruppe B. Nicht A, da die AVL-Eigenschaft in oberster Wurzel verletzt ist (−2).
(3): Gruppe C. Nicht D, da keine Inhalte vorhanden sind.
(4): Gruppe A. AVL-Eigenschaft überall ok und Inhalte (Binärzahlen) aufsteigend sortiert.
(5): Gruppe D. Nicht C, da es vom obersten Knoten drei Nachfolger gibt.
(6): Gruppe C. Nicht B, da die alphabetische Reihenfolge gestört ist ( L nicht kleiner K).
b) Die Lösungen der Teilaufgaben lauten wie folgt:
b.1) Das Einfügen gestaltet sich wie folgt:
4,2 6
R(4), L(1) 6
7,6,1
7 R(7) 6
6
1
1
7
2
7
4
1
L(2)
4
5
2
1
9
2
1
11
8
4
1
6
1
3
3
8
5
11
9
2
1
5
9
4
8
11
5
c) Eine Funktion, welche die AVL-Eigenschaft überprüft lautet wie folgt:
function TBinBaum.AVL_ok: boolean;
var l,r: integer;
begin
if leer then AVL_ok:= true
else begin
l:= LinkerTeilbaum.Baumhoehe;
r:= rechterTeilbaum.Baumhoehe;
AVL_ok:= LinkerTeilbaum.AVL_ok
RechterTeilbaum.AVL_ok
(abs(l-r) <= 1);
end;
end;
7
5
6
10
2
11
1
6
10
6
2
9
4
7
3
10
5
6
3
2
5
8
4
4
4
6
L(8)
3
7
1
3
2
1
6
2
5
1
b.1) Das Löschen gestaltet sich wie folgt:
7
6
7
4
4
7
2
1
2
R(6)
6
5
and
and
Die Hilfsfunktion für die Baumhöhe lautet wie folgt:
9
4
8
5
11
function TBinBaum.Baumhoehe: integer;
var l,r: integer;
begin
if leer then Baumhoehe:= 0
else begin
l:= LinkerTeilbaum.Baumhoehe;
r:= RechterTeilbaum.Baumhoehe;
if l>r then Baumhoehe:= l+1
else Baumhoehe:= r+1;
end;
end;
d) Die Speicherung geschieht PreOrder, d. h. in dem File wird beim Beispielbaum aus
Teilaufgabe c) die Zahlenreihenfolge 6, 3, 2, 1, 4, 5, 8, 7, 10, 9, 11 gespeichert.
Baut man nun mit dieser Zahlenreihenfolge wieder einen neuen Suchbaum auf, so ergibt
sich exakt der gleiche Suchbaum wie zuvor.
Ein Grobalgorithmus wäre wie folgt:
Solange Datei nicht am Ende
Tue: Lies nächste Zahl
Baum.Insert(Zahl)
Aufgabe 3:
Bäume - Quadtrees
Quadratische Schwarz-Weiß-Grafiken, deren Seitenlänge (in Pixeln) eine Zweierpotenz ist,
können mit Hilfe der Datenstruktur QuadTree gespeichert werden. Ein QuadTree ist dabei
ein Baum, der entweder vier Teilbäume hat oder ein Blatt ist.
Der Inhalt eines Bildes wird nach folgendem Algorithmus in der Datenstruktur abgelegt:
• Jedem Teilquadrat der Grafik entspricht ein QuadTree.
• Ist die Farbe eines Teilquadrates einheitlich, so wird in den Knoten des zugehörigen
QuadTree der Farbwert (1 = schwarz, 0 = weiß) eingetragen. Der QuadTree besitzt in
diesem Fall leere Teilbäume und ist somit ein Blatt.
• Ist die Farbe nicht einheitlich, so wird dies im Knoten des zugehörigen QuadTree
durch den Wert −1 kenntlich gemacht. Anschließend wird das Quadrat in vier
Teilquadrate zerlegt. Deren Bildinhalte werden im Uhrzeigersinn (links oben, rechts
oben, rechts unten, links unten) in Teilbäume abgelegt und an den aktuellen Knoten
angehängt.
Beispiel: Folgendes 8x8-Schwarz-Weiß-Bild soll in einem QuadTree gespeichert werden:
Der zugehörige QuadTree sieht demnach wie folgt aus:
−1
−1
1
−1
1
−1
0
0
0
0
0
0
0
0
1
−1
0
0
1
0
−1
0
1
0
Durchläuft man den Baum in der Reihenfolge Wurzel-Linksaußen-Linksinnen-RechtsinnenRechtsaußen (WLaLiRiRa), so erhält man die untenstehende Folge von Knoteninhalten, die
als Bilddatei gespeichert werden kann:
−1, −1, −1, 1, 0, 0, 0, 0, 0, 0, 1, −1, 0, 0, 1, 0, −1, 0, 1, 0, −1, 0, 1, 0, 0
a)
Stellen Sie den QuadTree zur rechts abgebildeten 8x8Schwarz-Weiß-Grafik dar. Geben Sie anschließend die Folge
von Knoteninhalten an, die als Bilddatei gespeichert werden
könnte.
0
b)
Eine 8x8-Grafik ist in Form einer Bilddatei (nach der oben genannten
Durchlaufstrategie WLaLiRiRa) abgelegt:
−1, −1, 0, −1, 0, 1, 1, 0, −1, 0, 1, 1, 0, 0, −1, −1, 1, 0, 0, 1, 0, 0, −1, 1, 0, 0, 1,
−1, 1, 1, 0, 0, −1, 0, 0, 1, 1
Stellen Sie den zugehörigen QuadTree grafisch dar.
Stellen Sie die zugehörige 8x8 Grafik dar.
Analysieren Sie, was passieren würde, wenn Sie mit dieser Datei statt einer 8x8-Grafik
eine 16x16-Grafik aufbauen würden?
Stellen Sie die zugehörige 16x16-Grafik dar und beschreiben Sie die Veränderungen
bzgl. der 8x8-Grafik.
Begründen Sie, ob es möglich ist, aus dieser Datei eine 4x4-Grafik aufzubauen und
erläutern Sie eventuelle Einschränkungen.
c)
Nebenstehend sehen Sie eine Modellierung der
Klasse TQuadTree.
Dokumentieren Sie die Klasse.
d)
Implementieren Sie die Methode
LoadFromFile(...) , welche den Aufbau
eines QuadTrees aus einer Datei mit dem angegebenen Dateinamen übernimmt.
Hinweis 1: Damit die Methode aufgerufen werden kann, muss die Wurzel des
QuadTrees bereits erzeugt sein (siehe Oberflächenprozedur). Berücksichtigen Sie dies
in Ihrer Implementierung.
Hinweis 2: Falls Sie die Methode LadeRekursiv_WLaLiRiRa in Ihrer
Implementierung benötigen, so implementieren Sie diese bitte ebenfalls.
type TQuadTreeFile = file of shortint;
procedure TQuadTree.LoadFromFile(dateiname: string);
var ...
begin
...
end;
procedure MI_OeffnenClick(Sender: TObject);
var QT: TQuadTree;
begin
QT:= TQuadTree.Create;
QT.LoadFromFile(´Baum.dat´);
end;
Lösung:
Aufgabe 3:
a) Zur angegebenen Grafik gehört der folgende QuadTree
−1
−1
0
1
−1
0
−1
0
0
–1
0
1
0
1
0
0
1
–1
0
1
1
0
1
0
1
Das Bild entspricht somit der Datei
−1, −1, −1, 0, 1, 0, 1, 0, 0, −1, 0, 1, 0, 1, 0, −1, 0, 1, 0, 1, −1, 1, 0, 1, 0
b.1) Der zugehörige QuadTree sieht wie folgt aus:
−1
–1
−1
0
1
0
–1
–1
0
1
0
0
1
1
0
1
–1
−1
–1
0
0
–1
0
0
1
1
1
0
1
0
0
0
0
0
1
1
1
b.2) Und das zugehörige 8x8-Bild hat folgendes Aussehen:
b.3) Würde man aus der Datei ein 16x16-Bild aufbauen, so wäre es um den Faktor 2
„gezoomt“:
b.4) Aus dieser Datei ist es nicht so ohne weiteres möglich, da der Baum eine Tiefe von 4
Ebenen hat. Die dritte Ebene entspricht aber bereits dem Farbwert eines Pixels, so dass
für die vierte Ebene ein Pixel aufgeteilt werden müsste. Dies ist jedoch nicht möglich.
Eine Möglichkeit bestände darin, die Farbwerte der Teilbäume aus der vierten Ebene zu
einem Farbwert für ein Pixel zusammenzufassen („Rendering“ durch
Mittelwertbildung). Diese Möglichkeit muss jedoch vom Prüfling nicht erkannt werden.
c)
Die Dokumentation der Klasse könnte wie folgt aussehen:
Anfrage LoadFromFile:
vorher: Baum mit einem Wurzelelement
nachher: Baum, welcher aus einer Datei ausgelesen wurde
Auftrag SaveToFile:
vorher: Baum exisitert und ist nicht leer
nachher: Der Baum wurde in der Traversierung WLaLiRiRa in die Datei gespeichert.
Auftrag Zeichnen:
vorher: Der Baum ist nicht leer
nachher: Der Baum wurde in der Paintbox ausgegeben
Anfrage Leer:
vorher: Der Baum exisitiert
nachher: Ist der Baum leer, so wird true zurück gegeben, andernfalls false
Auftrag LadeRekursiv_WLaLiRiRa
vorher: Die Datei ist geöffnet
nachher: Der Baum wurde aus der Datei eingelesen.
d) Lediglich der Dateiname wird übergeben, so dass eine Rahmenprozedur die Datei öffnen
muss und eine innere rekursive Prozedur die Inhalte PreOrder einlesen muss.
procedure TQuadTree.LadeRekursiv_WLaLiRiRa(var f:
TQuadTreeFile);
var i: integer;
begin
Readln(f,Farbwert);
if Farbwert=-1
then begin
for i:= 1 to 4
do begin
Teilbaeume[i]:= TQuadTree.Create;
Teilbaeume[i].LadeRekursiv_WLaLiRiRa(f);
end;
end;
end;
procedure TQuadTree.LoadFromFile(dateiname: string);
var datei : TQuadTreeFile;
begin
AssignFile(datei, dateiname);
ReSet(datei);
LadeRekursiv_WLaLiRiRa(datei);
CloseFile(datei);
end;
procedure MI_OeffnenClick(Sender: TObject);
var QT: TQuadTree;
begin
QT:= TQuadTree.Create;
QT.LoadFromFile(´Baum.dat´);
end;
Aufgabe 4:
Baumstrukturen – Suchbäume − AVL-Bäume
a) In einen anfangs leeren Suchbaum werden nacheinander die Elemente
10, 20, 30, 50, 40, 60, 70 eingefügt.
Zeichnen Sie den resultierenden Suchbaum.
b) Erläutern Sie an diesem Beispiel die Nachteile, die sich bei der Verwendung von
Suchbäumen ergeben können.
c) Die Elemente aus Aufgabenteil a) werden nun in einen anfangs leeren AVL-Baum
eingefügt.
Stellen Sie schrittweise die Entwicklung des AVL-Baums dar.
d) Die Elemente 10, 60, 50, 70 werden nacheinander aus dem AVL-Baum aus Teilaufgabe c)
gelöscht.
Stellen Sie schrittweise die Entwicklung des AVL-Baums dar.
e) Rechts abgebildet sehen Sie das UML-Diagramm der Klasse
TAVLTree.
Implementieren Sie die Methode getTreeHeight
Hinweis: Die Dokumentation der Klasse TBinTree finden
Sie in der ANLAGE IV
Lösung:
Aufgabe 4: a)
10
20
30
50
40
60
70
b) Man erkennt direkt, dass der Baum annähernd zu einer linearen Liste entartet. Die Vorteile
der geringen Suchtiefe von O(log n) in einem ausgeglichenen Suchbaum mit n Elementen
gehen verloren.
c)
20
10
10
30
20
50
30 LR(10)
20
10
40
40
20
50
30
10
60
40
20
30
30
60
70
LR(20)
50
LR(50)
70
40
40
20
60
30
50
60
10
d)
RR(50), LR(30)
40
20
50
40
20
70
30
70
40
70
30
50
20
30
30
LR(20) RR(40) 20
e) function TAVLTree.getTreeHeight: integer;
function hoehe(tree: TBinTree): integer;
var l, r: integer;
begin
if isEmpty()
then result:= 0
else begin
l:= hoehe(getLeftTree as TBinTree);
r:= hoehe(getRightTree as TBinTree);
if l>r then result:= l + 1
else result:= r + 1;
end;
end;
begin
result:= hoehe(pTree);
end;
40
Aufgabe 5:
AVL-Bäume
a) Die nachfolgend aufgeführten Knoteninhalte sollen in einen anfangs leeren AVLBaum eingefügt werden. Stellen Sie den schrittweisen Aufbau des AVL-Baums grafisch
dar. Zeichnen Sie auch nach jeder Korrekturoperation den neu entstandenen AVL-Baum.
45; 25; 15; 13; 12; 11; 8; 7; 50; 53; 35; 63
b) Löschen Sie aus dem nachfolgend angegebenen AVL-Baum sukzessive die
Knoteninhalte 16, 30, 40, 11. Geben Sie nach jedem Löschvorgang den entstandenen
Baum an.
30
18
37
10
7
5
23
11
20
16
33
25
40
32
21
c) Implementieren Sie eine Funktion TreeHeight(tree: TBinTree), welche die Höhe eines
Baumes berechnet und zurückgibt. Verwenden Sie wann immer möglich die Methoden
der Klasse TBinTree (Anlage I).
d) Implementieren Sie eine Funktion IsAVLTree(tree: TBinTree), welche überprüft, ob ein
Binärbaum bezüglich seiner Balancierung ein AVL-Baum ist (die Sortierung der
Knoteninhalte soll keine Rolle spielen). Verwenden Sie wann immer möglich die
Methoden der Klasse TBinTree (Anlage I).
Lösung:
Aufgabe 5:
a)
b)
c) function TreeHeight(tree: TBinTree): integer;
var l, r: integer;
begin
if tree.isEmpty()
then result := 0
else begin
l:= TreeHeight(tree.getLeftTree);
r:= TreeHeight(tree.getRightTree);
if l> r
then result := l + 1
else result := r + 1;
end;
end;
d) function IsAVLTree(tree: TBinTree): boolean;
var l, r: boolean;
balance: integer;
begin
if tree.isEmpty
then result:= true
else begin
l:= IsAVLTree(tree.getLeftTree);
r:= IsAVLTree(tree.getRightTree);
balance:= TreeHeight(tree.getRightTree) TreeHeight(tree.getLeftTree);
result := l and r and (balance in [-1,0,1];
end;
end;
Aufgabe 2:
Bäume
Ein Mobile besteht in der Regel aus Stangen, die untereinander mit Fäden verbunden sind und
an denen an beiden Enden je ein Anhänger befestigt ist. Im einfachsten Fall wollen wir jedoch
auch einen einzelnen Anhänger schon als Mobile auffassen. Folgende Fälle könne also für ein
Mobile unterschieden werden:
Normalfall: Mobile besteht aus mehreren Stangen,
Extremfall: Mobile besteht aus einem
an deren Ende entweder eine weitere Stange oder
Anhänger:
ein Anhänger hängt:
links
10mm
rechts
40mm
links
20mm
1200 g
5000 g
100 g
rechts
10mm
200 g
Bereits an diesem Beispiel erkennt man, dass der Normalfall eines Mobiles auf vielen
einzelnen kleinen Mobiles besteht. Als Informatiker können wir dies rekursiv formulieren:
Ein Mobile besteht aus
a) einem Anhänger (Rekursionsanker)
b) aus einer Stange, an denen sich zwei Teilmobiles befinden.
Im Fall a) interessiert uns lediglich die Masse (m) des Anhängers in Gramm.
Die Kraft (F), mit der ein solcher Anhänger an der Stange zieht berechnet sic nach der
physikalischen Vorschrift:F = m⋅⋅g, wobei für die Erdbeschleunigung g = 9,81 m/s2
angenommen werden kann.
Im Fall b) interessiert uns nur der Aufhängepunkt, also die Werte für die Abstände rechts und
links in Millimetern, wie in der Skizze gezeigt. Wir gehen der Einfachheit halber davon aus,
dass sowohl die Fäden als auch die Stangen gegenüber den Anhängergewichten ein
vernachlässigbares Gewicht haben.
Ein Mobile ist im Gleichgewicht, wenn das Hebelgesetz der Physik erfüllt ist:
Fl ⋅ links = Fr ⋅ rechts
a) Weisen Sie nach, dass sich das oben dargestellte Mobile überall im Gleichgewicht
befindet.
b) Zeichnen Sie ein Mobile mit mindestens 4 Stangen, welches sich überall im
Gleichgewicht befindet.
c) Zeichnen Sie jeweils ein UML-Klassendiagramm für die Klassen Anhaenger, Stange
und Mobile. Geben Sie im Klassendiagramm alle Datenfelder und Methoden an.
d) Zeichnen Sie nun ein neues Klassendiagramm ohne Methoden und Datenfelder für die
Klassen BinTree und die drei oben genannten Klassen. Veranschaulichen Sie in Ihrem
Diagramm die Beziehungen und begründen Sie Ihre Wahl.
e) Implementieren Sie eine Methode void beispiel(), welches das oben angegebene
Beispiel-Mobile erzeugt.
f) Implementieren Sie für die Klasse Mobile eine Methode int gesamtmasse(),
welche die Gesamtmasse des gespeicherten Mobiles berechnet.
g) Analysieren Sie die folgende Methode und geben Sie an, zu welchem Zweck diese
implementiert wurde.
private boolean rekursiveFunktion(BinTree b) {
if (b.isEmpty()) {
return true;
} else {
if (b.getRootItem() instanceof Anhaenger) {
return true;
} else {
if (b.getRootItem() instanceof Stange) {
return gesamtmasse(b.getLeftTree()) *
((Stange)b.getRootItem()).getLinks() ==
gesamtmasse(b.getRightTree()) *
((Stange)b.getRootItem()).getRechts()
&& rekursiveFunktion(b.getLeftTree())
&& rekursiveFunktion(b.getRightTree());
}
}
}
h) Implementieren Sie die folgenden drei Konstruktoren:
Mobile()
// leeres Mobile
Mobile(int gewicht)
// Mobile mit einem Anhänger
Mobile(Mobile m1, Mobile m1, int l1, int l2) // Mobile mit zwei Teilmobiles (m1
und m2), die an einer Stange in
den Entfernungen l1 bzw. l2
angebracht sind.
Die Klasse BinTree
In einem Objekt der Klasse BinTree werden beliebige Objekte nach dem Binärbaumprinzip
verwaltet. Ein Binärbaum ist entweder leer oder besteht aus einem Knoten, dem ein Element
und zwei binäre Teilbäume, die so genannten linken und rechten Teilbäume, zugeordnet
sind.
Dokumentation der Methoden der Klasse BinTree
Konstruktor BinTree()
nachher Ein leerer Baum existiert
Konstruktor BinTree (Object pObject)
nachher Der Binärbaum existiert und hat einen Wurzelknoten mit dem Inhalt pObject und
zwei leeren Teilbäumen.
Konstruktor BinTree (Object pObject, BinTree pLeftTree, BinTree
pRighttree)
nachher Der Binärbaum existiert, hat einen Wurzelknoten mit dem Inhalt pObject sowie
dem linken Teilbaum pLeftTree und dem rechten Teilbaum pRightTree.
Anfrage isEmpty(): boolean
nachher Diese Anfrage liefert den Wahrheitswert true, wenn der Binärbaum leer ist, sonst
liefert sie den Wert false.
Auftrag clear()
nachher Der Binärbaum ist leer.
Auftrag setRootItem (Object pObject)
nachher Die Wurzel hat – unabhängig davon, ob der Binärbaum leer ist oder schon eine
Wurzel hat – pObject als Inhalt. Eventuell vorhandene Teilbäume werden nicht
geändert.
Anfrage getRootItem(): Object
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den Inhalt des Wurzelknotens des Binärbaums.
Auftrag setLeftTree (BinTree pTree)
vorher
Der Binärbaum ist nicht leer.
nachher Die Wurzel hat den übergebenen Baum als linken Teilbaum.
Auftrag setRightTree (BinTree pTree)
vorher
Der Binärbaum ist nicht leer.
nachher Die Wurzel hat den übergebenen Baum als rechten Teilbaum.
Anfrage getLeftTree(): BinTree
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den linken Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Anfrage getRightTree(): BinTree
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den rechten Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Lösung:
Aufgabe 2:
Baumstrukturen
a) 100g⋅20mm⋅9,81m/s2 = 200g⋅100mm⋅9,81m/s2
1200g⋅10mm⋅9,81m/s2 = 300g⋅40mm⋅9,81m/s2
b) verschiedene Lösungen sind denkbar…
c) und d)
und
e)
public void beispiel() {
Anhaenger a1 = new Anhaenger(1200);
Anhaenger a2 = new Anhaenger(100);
Anhaenger a3 = new Anhaenger(200);
Stange s1 = new Stange(10,40);
Stange s2 = new Stange(20,10);
tree = new BinTree(s1,
new BinTree(a1),
new BinTree(s2,
new BinTree(a2),
new BinTree(a3)
)
);
}
f)
private int gesamtmasse(BinTree b) {
if (b.isEmpty()) {
return 0;
} else {
if (b.getRootItem() instanceof Anhaenger) {
return ((Anhaenger)b.getRootItem()).getGewicht();
} else {
if (b.getRootItem() instanceof Stange) {
return gesamtmasse(b.getLeftTree()) +
gesamtmasse(b.getRightTree());
} else {
throw new RuntimeException("Kein Mobilebaum!");
}
}
}
}
public int gesamtmasse() {
try {
return gesamtmasse(tree);
} catch (Exception e) {
System.err.println(e.toString());
return -1;
}
}
2.
h)
g) Die Methode überprüft, ob sich das gesamte Mobile im Gleichgewicht befindet. Dazu
werden zwei Fälle unterschieden:
1. Besteht das Mobile nur aus einem Anhänger, so befindet sich das Mobile natürlich im
Gleichgewicht.
Ist das Mobile eine Stange, so wird zunächst geprüft, ob sich das Mobile am aktuellen
Knoten im Gleichgewicht befindet (gesamtmasse*laenge auf beiden Seiten gleich).
Anschließend wird noch geprüft, ob sich die beiden Teilmobiles, die links und rechts an
der Stange hängen, ebenfalls im Gleichgewicht befinden. Ist dies der Fall, so befindet
sich das gesamte Mobile im Gleichgewicht, andernfalls nicht.
public Mobile() {
tree = new BinTree();
}
public Mobile(int gewicht) {
tree = new BinTree(new Anhaenger(gewicht));
}
public Mobile(Mobile m1, Mobile m2, int l1, int l2) {
Stange stange = new Stange(l1, l2);
tree = new BinTree(stange, m1.getTree(), m2.getTree());
}
Aufgabe 2:
Binärbäume
Im Morsealphabet ist jeder Buchstabe durch eine Aneinanderreihung von Punkten (·) und
Strichen (-) codiert. Die Codierung der Buchstaben lautet wie folgt:
Morsealphabet
A ·N -·
B -·· C -·-· D -··· E ·
F ··-· G --· H ···· I ··
J ·--- K -·- L ·-·· M –O --- P ·--· Q --·- R ·-· S ··· T U ··- V ···- W ·-- X -··- Y -·-- Z --··
Eine Möglichkeit, den Morse-Code zu verwalten, ist die Speicherung der Codes in einem
Binärbaum. Der Morsepunkt bewirkt dabei eine Verzweigung nach
links, der Morsestrich eine Verzweigung nach rechts. In jedem
•
Knoten des Baumes steht der Buchstabe, der durch den im Baum
vorausgegangenen Punkt-Strich-Code repräsentiert wird.
...
a) Stellen Sie den Morsecode wie oben beschrieben als Baum dar.
−
Der Pfad zum Buchstaben R müsste in Ihrem Baum wie rechts
abgebildet aussehen.
...
•
b) Übersetzen Sie den folgenden Morsetext. Die |-Zeichen trennen die
einzelnen Buchstaben.
-|---|·-··|·-··|·|-·-|·-··|·-|··-|···|··-|·-·
c)
R
Beschreiben Sie, wie man algorithmisch vorgeht, wenn man einen Text mit Hilfe des
Baumes übersetzen möchte.
d) Begründen Sie, warum eine Trennung der einzelnen Buchstaben (hier durch das
Zeichen | ) erforderlich ist.
Folgende Datenstruktur soll im folgenden für die Speicherung des Morsebaumes verwendet
werden:
Die Morsecodes der Buchstaben 'A' bis 'Z' sind dabei im Datenfeld codeTabelle wie folgt
gespeichert:
private static String[] codeTabelle = {
".-","-..","-.-.","-...",".","..-.","--.","....","..",".---","-.-",".-..","--",
"-.","---",".--.","--.-",".-.","...","-","..-","...-",".--","-..-","-.--","--.."
};
e) Analysieren Sie die folgende Methode, d. h., erläutern Sie ihre Arbeitsweise, indem Sie die
wesentlichen Zeilen bzw. Abschnitte kommentieren, und erläutern Sie kurz die zentrale
Funktionalität der Methode.
...
Hinweis: Objekte der Klasse Character enthalten lediglich ein Datenfeld vom Typ char,
welches mithilfe der Methode char charValue() geholt werden kann.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
f)
private void xxx() {
this.baum = new BinTree();
BinTree hilf;
for (char buchstabe = 'A'; buchstabe <= 'Z'; buchstabe++) {
hilf = baum;
String morseCode = codeTabelle[(int)buchstabe-(int)'A'];
for (int i = 0; i < morseCode.length(); i++) {
if (hilf.isEmpty()) {
hilf.setRootItem(new Character(' '));
}
if (morseCode.charAt(i) == '.') {
hilf = hilf.getLeftTree();
} else {
hilf = hilf.getRightTree();
}
}
hilf.setRootItem(new Character(buchstabe));
}
}
Implementieren Sie die Methode String klarToMorse(char klar) der Klasse
Morsebaum.
Hinweis: Die Methode gibt zu einem Klartextzeichen (klar) den entsprechenden
Morsecode zurück. Ist das Zeichen klar kein Buchstabe, so soll der Morsecode leer
("") bleiben.
klarToMorse(´V´) liefert den Morsecode "···−".
klarToMorse(´#´) liefert den Morsecode "".
g) Implementieren Sie die Methode char morseToKlar(String morse) der Klasse
Morsebaum, ohne dabei das private Datenfeld codeTabelle zu nutzen.
Hinweis: Die Methode gibt zu einem Morsecode (morse) den entsprechenden
Buchstaben im Klartext zurück.
Sie können davon ausgehen, dass in der Variablen morse ein gültiger Morsecode eines
Buchstabens enthalten ist.
morseToKlar("··-·") liefert den Buchstaben ´F´.
ANLAGEN:
Dokumentation der Methoden der Klasse BinTree
Konstruktor BinTree()
nachher Ein leerer Baum existiert
Konstruktor BinTree (Object pObject)
nachher Der Binärbaum existiert und hat einen Wurzelknoten mit dem Inhalt pObject und
zwei leeren Teilbäumen.
Konstruktor BinTree (Object pObject, BinTree pLeftTree, BinTree
pRighttree)
nachher Der Binärbaum existiert, hat einen Wurzelknoten mit dem Inhalt pObject sowie
dem linken Teilbaum pLeftTree und dem rechten Teilbaum pRightTree.
Anfrage isEmpty(): boolean
nachher Diese Anfrage liefert den Wahrheitswert true, wenn der Binärbaum leer ist, sonst
liefert sie den Wert false.
Auftrag clear()
nachher Der Binärbaum ist leer.
Auftrag setRootItem (Object pObject)
nachher Die Wurzel hat – unabhängig davon, ob der Binärbaum leer ist oder schon eine
Wurzel hat – pObject als Inhalt. Eventuell vorhandene Teilbäume werden nicht
geändert.
Anfrage getRootItem(): Object
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den Inhalt des Wurzelknotens des Binärbaums.
Auftrag setLeftTree (BinTree pTree)
vorher
Der Binärbaum ist nicht leer.
nachher Die Wurzel hat den übergebenen Baum als linken Teilbaum.
Auftrag setRightTree (BinTree pTree)
vorher
Der Binärbaum ist nicht leer.
nachher Die Wurzel hat den übergebenen Baum als rechten Teilbaum.
Anfrage getLeftTree(): BinTree
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den linken Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Anfrage getRightTree(): BinTree
vorher
Der Binärbaum ist nicht leer.
nachher Diese Anfrage liefert den rechten Teilbaum der Wurzel des Binärbaums. Der
Binärbaum ist unverändert.
Lösung:
Aufgabe 2:
Binärbäume
a) Der Morsebaum hat folgende Struktur:
b) Der Text lautet: TOLLEKLAUSUR
c) Initialisiere den Klartext mit einer leeren Zeichenkette
Lies den Morsetext Zeichen für Zeichen.
Liest man das Zeichen | so
Erweitere den Klartext um das Zeichen im aktuellen Knoten des Baums
Starte wieder an der obersten Wurzel Morsebaums.
Liest man das Zeichen . so
Gehe im Morsebaum nach links runter
Liest man das Zeichen – so
Gehe im Morsebaum nach rechts runter
d) Die Trennungszeichen sind nötig, da man sonst z. B. den Morsecode "---" als den
Buchstaben O aber auch als dreimal den Buchstaben T interpretieren könnte.
e) Die Methode xxx erstellt zunächst einen leeren Binärbaum.
Für alle Buchstaben von A bis Z wird jedes mal am Anfang des (zu Anfang leeren)
Binärbaums gestartet. In der Variablen morsecode steht der zum Zeichen zugehörige
Morsecode.
Ist der Baum noch leer, so wird zunächst eine Wurzel mit unbekanntem Zeichen angelegt.
Danach wird je nach aktuellem Zeichen des zum Buchstaben zugehörigen Morsecodes im
Binärbaum nach links oder rechts verzweigt.
Hat man den ganzen Morsecode abgearbeitet (Ende der for-Schleife), so kann in die
Wurzel des aktuellen Knotens der aktuelle Buchstabe geschrieben werden. Evtl. wird
dabei ein unbekanntes Zeichen aus einem vorherigen Schleifendurchlauf überschrieben.
Die Methode baut den Morsecode für alle Buchstaben von A bis Z auf.
f)
public String klarToMorse(char klar) {
klar = Character.toUpperCase(klar);
if (klar >= 'A' && klar <= 'Z'){
return codeTabelle[(int)klar-(int)'A'];
} else {
return ""+klar;
}
}
g)
public char morseToKlar(String morse) {
BinTree hilf = baum;
for (int i = 0; i < morse.length(); i++) {
if (morse.charAt(i) == '.') {
hilf = hilf.getLeftTree();
} else {
if (morse.charAt(i) == '-') {
hilf = hilf.getRightTree();
} else {
return morse.charAt(0);
}
}
}
return ((Character)(hilf.getRootItem())).charValue();
}