Übungen zu Programmierung I - Blatt 8

Ähnliche Dokumente
Einführung in die Programmierung (EPR)

Vorlesung Datenstrukturen

Teilprüfung Software- und Internettechnologie Programmierkurs 2 Wintersemester 2004/2005

Datenstrukturen. einfach verkettete Liste

Algorithmen und Datenstrukturen. Bäume. M. Herpers, Y. Jung, P. Klingebiel

Übungspaket 32 Einfach verkettete, sortierte Liste

Humboldt-Universität zu Berlin Berlin, den Institut für Informatik

Beispiellösung zu den Übungen Datenstrukturen und Algorithmen SS 2008 Blatt 5

Tutoraufgabe 1 (Implementierung eines ADTs):

Algorithmen und Datenstrukturen (für ET/IT)

Kapitel 12: Induktive

Grundlagen der Informatik

Übung Datenstrukturen. Bäume

Klausur Kompaktkurs Einführung in die Programmierung Dr. T. Weinzierl & M. Sedlacek 18. April 2012

Algorithmen und Datenstrukturen (für ET/IT) Wiederholung: Ziele der Vorlesung. Wintersemester 2012/13. Dr. Tobias Lasser

elementare Datenstrukturen

13. Bäume: effektives Suchen und Sortieren

Anwendungsbeispiel MinHeap

13. Bäume: effektives Suchen und Sortieren

11.1 Grundlagen - Denitionen

Lösungshinweise/-vorschläge zum Übungsblatt 13: Software-Entwicklung 1 (WS 2017/18)

Binäre Suchbäume. Mengen, Funktionalität, Binäre Suchbäume, Heaps, Treaps

Vorlesung Datenstrukturen

Verkettete Datenstrukturen: Listen

Programmierung 1 (Wintersemester 2015/16) Wiederholungstutorium Lösungsblatt 15 (Linearer Speicher, Listen, Bäume)

Beispiellösungen zu den Übungen Datenstrukturen und Algorithmen SS 2008 Blatt 6

Algorithmen und Datenstrukturen

Einführung in die Informatik 2

Advanced Programming in C

3. Übungsblatt zu Algorithmen I im SoSe 2017

Informatik II, SS 2014

Algorithmen und Datenstrukturen

Mengen. Binäre Suchbäume. Mengen: Anwendungen (II) Mengen: Lösung mit Listen 12/3/12. Mengen, Funktionalität, Binäre Suchbäume, Heaps, Treaps

7. Dynamische Datenstrukturen Bäume. Informatik II für Verkehrsingenieure

Tutoraufgabe 1 (2 3 4 Bäume):

Fortgeschrittene Programmiertechnik Klausur WS 2014/15 Angewandte Informatik Bachelor

Wintersemester 2004/ Januar Aus der Vorlesung sind Datenstrukturen zur Repräsentation von Wäldern disjunkter Mengen bekannt.

Einführung in die Objektorientierte Programmierung Vorlesung 18: Lineare Datenstrukturen. Sebastian Küpper

Technische Universita t Mu nchen Institut fu r Informatik

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 16/17. Kapitel 14. Bäume. Bäume 1

Lösungsvorschläge zur Hauptklausur 1661 Datenstrukturen I

Übung Algorithmen und Datenstrukturen

Tutoraufgabe 1 (Listen):

9. Natürliche Suchbäume

Übung Informatik I - Programmierung - Blatt 8

11. Elementare Datenstrukturen

Vorlesung Informatik 2 Algorithmen und Datenstrukturen

Tutoraufgabe 1 (2 3 4 Bäume):

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 17/18. Kapitel 14. Bäume. Bäume 1

Programmiertechnik II Klausur SS 2018 Angewandte Informatik Bachelor

Abgabe: (vor der Vorlesung) Aufgabe 3.1 (P) Master-Theorem

1 Der Baum. Informatik I: Einführung in die Programmierung 11. Bäume. Bäume in der Informatik. Bäume in der Informatik - Definition.

Nachname: Vorname: Matr.-Nr.: Punkte: 1. Aufgabe: ( / 25 Pkt.) Gegeben ist das folgende Struktogramm zur Berechnung von sin(x) mit Hilfe einer Reihe.

Vordiplom für Wirtschaftswissenschaften Allgemeine Informatik II SS Juli 2002 Bearbeitungszeit: 120 Minuten BEISPIELLÖSUNG

Informatik II, SS 2016

Informatik II, SS 2014

18. Natürliche Suchbäume

Informatik II, SS 2014

Informatik II, SS 2018

Prüfung A Informatik D-MATH/D-PHYS :15 14:55

1 Der Baum. Informatik I: Einführung in die Programmierung 11. Bäume. Bäume in der Informatik. Bäume in der Informatik - Definition.

Prüfung Informatik D-MATH/D-PHYS :00 17:00

Grundlagen der Informatik

Klausur Kompaktkurs Einführung in die Programmierung Dr. T. Weinzierl & M. Sedlacek 25. März 2011

Binärbäume: Beispiel

Lineare Liste. struct list_element { float f; /* weitere Elemente */ struct list_element *next; /* Zeiger auf Nachfolger-Element */ }; Peter Sobe

Informatik II, SS 2014

Klausur Algorithmen und Datenstrukturen

TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK

Einführung in die Informatik: Programmierung und Software-Entwicklung, WS 15/16. Kapitel 13. Bäume. Bäume 1

Programmierkurs Java

Informatik II Vorlesung am D-BAUG der ETH Zürich

Algorithmen und Datenstrukturen 2

Algorithmen und Datenstrukturen Heapsort

Übung 4: Die generische Klasse AvlBaum in Java 1

Klausur Algorithmen und Datenstrukturen

Informatik II, SS 2014

Informatik II, SS 2016

Algorithmen und Datenstrukturen (für ET/IT)

Datenstrukturen und Algorithmen (SS 2013)

C- Kurs 09 Dynamische Datenstrukturen

Bereichsabfragen. Dr. Martin Nöllenburg Vorlesung Algorithmische Geometrie

Rotation. y T 3. Abbildung 3.10: Rotation nach rechts (analog links) Doppelrotation y

Algorithmen und Datenstrukturen

Teil 1: Suchen. Ausgeglichene Bäume B-Bäume Digitale Suchbäume. M.O.Franz, Oktober 2007 Algorithmen und Datenstrukturen - Binärbäume 1-1

Algorithmen und Datenstrukturen

Grundlagen der Informatik / Algorithmen und Datenstrukturen. Aufgabe 132

Informatik II Bäume zum effizienten Information Retrieval

Datenstrukturen & Algorithmen Lösungen zu Blatt 6 FS 14

Abiturprüfung Informatik, Grundkurs

Informatik II Übung 5

Fortgeschrittene Programmiertechnik Klausur SS 2015 Angewandte Informatik Bachelor

Transkript:

Dr. G. Zachmann A. Greß Universität Bonn Institut für Informatik II 1. Dezember 2004 Wintersemester 2004/2005 Übungen zu Programmierung I - Blatt 8 Abgabe am Mittwoch, dem 15.12.2004, 15:00 Uhr per E-Mail Aufgabe 1 (Pointer auf Structs, 3 Punkte) Gegeben seien die folgenden Definitionen der Structs Person und Abteilung: s t r u c t Person s t r u c t A b t e i l u n g { { i n t p e r s o n a l N r ; i n t a b t e i l u n g s N r ; A b t e i l u n g a b t e i l u n g ; Person l e i t e r ; } ; } ; Schreiben Sie eine Funktion, welche eine Person (oder alternativ einen Pointer auf eine Person) als Parameter erhält und für diese Person folgende Daten ausgibt: ihre Personalnummer die Nummer der Abteilung, bei der die Person angestellt ist ob die Person Leiter dieser Abteilung ist (Hinweis: Pointer-Vergleich) falls nicht, die Personalnummer des zugehörigen Abteilungsleiters Schreiben Sie zum Testen dieser Funktion ein Hauptprogramm, welches die folgenden Variablen-Belegungen verwendet und die obige Funktion für alle fünf Personen aufruft: Person anton, berta, c a e s a r, dora, e m i l ; A b t e i l u n g l a g e r, v e r k a u f ; anton. p e r s o n a l N r = 105; anton. a b t e i l u n g = & l a g e r ; b e r t a. p e r s o n a l N r = 114; b e r t a. a b t e i l u n g = & l a g e r ; c a e s a r. p e r s o n a l N r = 1 2 3 ; c a e s a r. a b t e i l u n g = & l a g e r ; dora. p e r s o n a l N r = 132; dora. a b t e i l u n g = & v e r k a u f ; e m i l. p e r s o n a l N r = 141; e m i l. a b t e i l u n g = & v e r k a u f ; l a g e r. a b t e i l u n g s N r = 1 ; l a g e r. l e i t e r = & b e r t a ; v e r k a u f. a b t e i l u n g s N r = 2 ; v e r k a u f. l e i t e r = & dora ; Hinweis: Aufgrund der gegenseitigen Abhängigkeit in den Definitionen der Structs Person und Abteilung benötigen Sie eine Forward Declaration 1. 1 Zur Erinnerung: Im allgemeinen müssen Structs in C++ vor ihrer Benutzung definiert worden sein. Um innerhalb einer Variablen- bzw. Member-Definition einen Pointer auf eine Struct verwenden zu können (wie in obigem Beispiel), genügt es aber auch, diese Struct vorher zu deklarieren (Forward Declaration). Analog zur Funktions-Deklaration gibt die Forward Declaration einer Struct nur den Namen der Struct an, nicht aber deren Inhalt (Member). Die Syntax der Forward Declaration lautet für eine Struct namens S wie folgt (man beachte die fehlenden geschweiften Klammern): struct S; 1

Aufgabe 2 (Zirkulär doppelt verkettete Listen, 10 Punkte) Im Gegensatz zur der in der Vorlesung vorgestellten einfach verketteten Liste enthalten die Knoten einer doppelt verkettete Liste nicht nur Zeiger auf die nachfolgenden Knoten, sondern auch Zeiger auf die vorausgehenden Knoten. In dieser Aufgabe soll die folgende Struct verwendet werden, welche einen Knoten der Liste repräsentiert: s t r u c t Node { i n t data ; Node l e f t ; Node r i g h t ; } data ist ein im Knoten gespeicherter Datenwert. left ist der Zeiger auf den vorausgehenden Knoten der Liste, right der Zeiger auf den nachfolgenden Knoten. In dieser Aufgabe soll eine zirkulär doppelt verketteten Liste verwendet werden, d.h. der left -Zeiger des Listenkopfes (also desjenigen Elementes der Liste, welches wir als erstes Elements auszeichnen) zeigt auf das Listenende und der right -Zeiger des Listenendes auf den Listenkopf, s. Abb. 1 (Somit sind die left / right-zeiger aller Knoten der Liste ungleich NULL.) a) Grundlegende Operationen auf Listen 1. Schreiben Sie eine Funktion Abbildung 1: Eine zirkulär doppelt-verkettete Liste Node newlistnode ( i n t data ) welche eine ein-elementige zirkulär doppelt-verkettete Liste erzeugt (s. Abb. 2), und dessen Kopf (welcher der einzige Knoten dieser Liste ist) zurückgibt. Benutzen Sie dabei den new-operator, um den Knoten der Liste dynamisch zu allozieren: Node n = new Node ; Der Funktionsparameter data bestimmt den Inhalt des Datenfeldes dieses Knotens. 2. Schreiben Sie eine Funktion Node a p p e n d L i s t s ( Node l i s t H e a d 1, Node l i s t H e a d 2 ) welche zwei Listen hintereinanderhängt, deren Köpfe durch die Parameter listhead1 und listhead2 gegeben sind, und einen Zeiger auf den Kopf der resultierenden zusammenhängenden Liste zurückgibt. Wird der Funktion ein NULL-Zeiger als einer der beiden Parameter übergeben, soll dies als eine leere Liste interpretiert werden. 2

Abbildung 2: Eine ein-elementige zirkulär doppelt-verkettete Liste 3. Schreiben Sie eine Funktion void p r i n t L i s t ( Node l i s t H e a d ) welche die in der verketteten Liste enthaltenen Datenwerte der Reihe nach auf der Standardausgabe ausgibt, beginnend mit dem als Parameter listhead übergebenen Listenkopf. 4. Schreiben Sie ein Hauptprogramm, welches von der Standardeingabe Integer-Werte einliest und dabei eine Liste aufbaut, welche diese Werte in der eingegebenen Reihenfolge enthält. Verwenden Sie dabei die Funktionen newlistnode() und appendlists(), um neue Knoten an die Liste anzuhängen. Sobald eine negative Zahl eingelesen wurde, soll die Konstruktion der Liste abgebrochen und der Inhalt der Liste mittels der Funktion printlist () wieder ausgegeben werden. (Es sollen also letzlich die eingelesen Werte wieder in derselben Reihenfolge ausgegeben werden.) Anm.: In dieser (und der folgenden) Aufgabe müssen Sie den allozierten Speicher nicht wieder freigegeben. b) Bubble Sort Ergänzen Sie das Programm aus Aufgabenteil a) wie folgt: Schreiben Sie eine Funktion Node s o r t L i s t ( Node l i s t H e a d ) welche die übergebene Liste aufsteigend sortiert, und einen Zeiger auf den Kopf der sortierten Liste (welcher den kleinsten Datenwert der Liste enthält) zurückgibt. Verwenden Sie dazu den folgenden Algorithmus (Bubble Sort): sorted false while not sorted do sorted true for all E L do if (E is not last node in L) (E.x > E next.x) then swap(e,e next ) sorted false end if end for end while Dabei bezeichnet E jeweils einen Knoten der Liste L und E next den jeweiligen nachfolgenden Knoten (welcher über den right -Zeiger erreicht wird). swap(e,e next ) besagt demnach, dass die Reihenfolge zweier benachbarter Knoten der Liste vertauscht werden soll. Hinweis: Im swap()-schritt können Sie einfach die Datenwerte der beiden Knoten vertauschen. (Alternativ können Sie aber auch die Zeiger umhängen.) Beachten Sie, dass der letzte Knoten in der Liste derjenige ist, dessen right -Zeiger auf den Listenkopf zeigt. Ergänzen Sie das Hauptprogramm aus Aufgabenteil a) um den Aufruf von sortlist (), so dass die eingelesene Liste in sortierter Reihenfolge wieder ausgegeben wird. 3

Aufgabe 3 (Binäre Bäume, 11 Punkte) Ein binärer Baum kann wie folgt rekursiv beschrieben werden: Ein binärer Baum B besteht aus einem Wurzelknoten w, welcher bis zu zwei Kinderknoten hat, die selber wieder Wurzelknoten binärer Bäume bilden (nämlich des linken bzw. rechten Teilbaumes des Knotens w). Außer dem Wurzelknoten w (der keinen Vaterknoten hat) haben alle Knoten des Baumes einen eindeutigen Vaterknoten. Ein Knoten, der keine Kinderknoten hat, wird Blatt genannt. Demgegenüber wird ein Knoten, der mindestens einen Kinderknoten hat, innerer Knoten genannt. In dieser Aufgabe soll die folgende Struct verwendet werden, um einen Knoten des Baumes zu repräsentieren: s t r u c t Node { i n t data ; Node l e f t ; Node r i g h t ; } Hierbei ist, anders als in Aufg. 2, left der Zeiger auf den Wurzelknoten des linken Teilbaums und right der Zeiger auf den Wurzelknoten des rechten Teilbaums. data ist wiederum ein im Knoten gespeicherter Datenwert. Falls left == NULL oder right == NULL besitzt der Knoten keinen linken bzw. rechten Teilbaum. (Bei einem Blatt sind demnach beide Zeiger NULL.) In dieser Aufgabe soll ein sortierter binärer Baum aufgebaut werden, d.h. für jeden inneren Knoten n des Baumes soll gelten: alle Knoten des linken Teilbaumes von n (insofern vorhanden) enthalten nur Datenwerte n. data und alle Knoten des rechten Teilbaumes von n (insofern vorhanden) enthalten nur Datenwerte > n.data (s. Abb. 3). a) Grundlegende Operationen auf Bäumen 1. Schreiben Sie eine Funktion Abbildung 3: Eine sortierter binärer Baum Node newleafnode ( i n t data ) welche einen nur aus einem Blatt bestehenden Baum erzeugt und dessen Wurzelknoten (also das Blatt) zurückgibt. Der Parameter data bestimmt den Inhalt des Datenfeldes dieses Knotens. (Verwenden Sie den new-operator, um den Knoten dynamisch zu allozieren.) 4

2. Schreiben Sie eine Funktion Node i n s e r t L e a f N o d e ( Node treeroot, i n t data ) welche in einen sortierten Baum, dessen Wurzelknoten durch den Parameter treeroot gegeben ist, ein neues Blatt mit dem durch den Parameter data gegebenen Datenwert einfügt und den Wurzelknoten des resultierenden sortierten Baumes zurückgibt. Ein leerer Baum wird durch treeroot == NULL repräsentiert (in diesem Fall muss also ein nur aus einem Blatt bestehender Baum zurückgegeben werden). Hinweis: Sie können diese Funktion wahlweise rekursiv oder iterativ implementieren. Erzeugen Sie das neue Blatt mit der obigen Funktion newleafnode(), und nutzen Sie die Voraussetzung, dass der gegebene Baum bereits sortiert ist, um einen geeigneten Vaterknoten für dieses Blatt zu finden. 3. Schreiben Sie eine rekusive Funktion void p r i n t T r e e ( Node t r e e R o o t ) welche die Datenwerte, die in dem durch den Wurzelknoten treeroot gegebenen sortierten Baum enthalten sind, in korrekter Reihenfolge auf der Standardausgabe ausgibt (also so, dass die Datenwerte tatsächlich in aufsteigender Sortierung ausgegeben werden). Hinweis: Achten Sie auf die korrekte Reihenfolge der rekusiven Aufrufe (und der Ausgabe) innerhalb der Funktion. 4. Schreiben Sie ein Hauptprogramm, welches von der Standardeingabe Integer-Werte einliest und dabei mit Hilfe obiger Funktionen einen binären Baum aufbaut, welcher diese Werte in aufsteigender Sortierung enthält. Sobald eine negative Zahl eingelesen wurde, soll die Konstruktion des Baumes abgebrochen und der Inhalt des sortierten binären Baumes mittels der Funktion printtree () ausgegeben werden. Anm.: Auch in dieser Aufgabe müssen Sie den allozierten Speicher nicht wieder freigegeben. b) Tree to List Obwohl der sortierte binäre Baum aus dieser Aufgabe und die zirkulär doppelt-verkettete Liste aus Aufg. 2 eigentlich zwei grundverschiedene Datenstrukturen sind, fällt auf, dass wir zur Repräsentation der Knoten bei beiden Datenstrukturen eine identisch aufgebaute Struct verwendet haben: bestehend aus einem Datenfeld und zwei Zeigern. Der einzige Unterschied liegt in der Bedeutung der left - und right -Zeiger. Eine interessante Aufgabe besteht nun darin, einen bestehenden sortierten binären Baum in eine zirkulär doppelt-verkette Liste umzubauen, indem man die in den Knoten enthaltenen Zeiger entsprechend umbiegt. Die Liste soll dabei so aufgebaut werden, dass die Sortierung erhalten bleibt (s. Abb. 4). Ein solches Umbauen des binären Baumes in eine zirkulär doppelt-verkettete Liste ist mit einer rekursiven Funktion in linearer Laufzeit (bzgl. der Anzahl Knoten) möglich: jeder Knoten muss insgesamt nur einmal bearbeitet werden. Schreiben Sie also eine rekursive Funktion Node t r e e T o L i s t ( Node t r e e R o o t ) welche, ausgehend von einem sortierten binären Baum mit Wurzelknoten treeroot, die left - und right - Zeiger der Knoten des Baumes so abändert, dass aus diesen Knoten eine zirkulär doppelt-verkettete Liste gebildet wird (unter Aufrechterhaltung der Sortierung). Die Funktion soll einen Zeiger auf den Kopf dieser Liste zurückgeben. 5

Abbildung 4: Umbauen des Baumes in eine Liste: Die left - und right -Zeiger des Baumes (vor der Umformung) sind in schwarz eingezeichnet. Die farbigen Pfeile zeigen die left - und right -Zeiger nach der Umformung in eine Liste. Beachten Sie, dass man, wenn man ausgehend vom Listenkopf head den rot eingezeichneten right -Zeigern folgt, die Zahlen 1 bis 5 in aufsteigender Reihenfolge besucht, genau wie in Abb. 1. Achtung: Sie dürfen hierbei weder neue Knoten allozieren noch die Datenwerte der Knoten umkopieren. Stattdessen sollen nur die left - und right -Zeiger der bereits bestehenden Knoten abgeändert werden. Hinweis: Die Rekursion ist der Schlüssel zur Lösung. Vertrauen Sie darauf, dass der rekursive Aufruf für beide Teilbäume korrekt arbeitet und konzentrieren Sie sich darauf, die resultierenden Listen dieser rekursiven Aufrufe mit dem Vaterknoten korrekt zu einer neuen Liste zusammenzusetzen. Sie können dabei die Funktion appendlists() aus Aufg. 2a) verwenden. Ergänzen Sie dann das Hauptprogramm aus Aufg. 3a), so dass der sortierte binäre Baum mit der Funktion treetolist () in eine sortierte Liste umgebaut wird, und diese Liste anschliessend mit der Funktion printlist () aus Aufg. 2a) ausgegeben wird. 6