Informatik/Jahrgangsstufe Q/001 Klausuren/Themensortiert/03
Transcription
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(); }