Technische Universität München Institut für Informatik Lehrstuhl für Computer Graphik & Visualisierung WS 2010 Praktikum: Grundlagen der Programmierung Lösungsblatt 7 Prof. R. Westermann, A. Lehmann, R. Fraedrich, F. Reichl Anmerkung: Da viele Fragen zur Diskussion anregen sollen, sind die hier vorgestellten Antworten lediglich als Lösungsskizze zu verstehen. Datenstrukturen II 7.1 (Ü) Fragen zu Datenstrukturen (a) Was versteht man grundsätzlich unter dem FIFO- bzw. LIFO-Prinzip? (b) Was ist ein Stack (Keller)? Was ist eine Queue (Schlange)? Wo liegen Gemeinsamkeiten und Unterschiede? (c) Beschreiben Sie, wie Sie grundsätzlich die Operationen für das Finden, Einfügen und Entfernen von Elementen in Stacks bzw. Queues implementieren würden. (a) FIFO: first-in-first-out Die zuerst zu der Datenstruktur hinzugefügten Elemente werden zuerst wieder entfernt bzw. herausgenommen (entspricht der Warteschlange im Supermarkt). LIFO: last-in-first-out Die zuletzt zu der Datenstruktur hinzugefügten Elemente werden zuerst wieder entfernt bzw. herausgenommen (entspricht einem Bücherstapel). (b) Ein Stack arbeitet nach dem LIFO-, eine Queue nach dem FIFO-Prinzip. Beide können (je nach Implementierung) grundsätzlich für beliebige Datentypen verwendet werden. Es ist beiden Fällen nicht möglich, auf beliebige Elemente der Datenstruktur zuzugreifen und dabei das FIFO-/LIFO-Prinzip zu verletzen. (c) Sowohl Stacks als auch Queues lassen sich sehr gut als verkettete Listen mit beschränktem Zugriff implementieren. Stack: push: Einfügen am Anfang der Liste pop: Entfernen vom Anfang der Liste 1
Queue: enqueue: Anhängen am Ende der Liste dequeue: Entfernen am Anfang der Liste Bei der Implementierung empfiehlt es sich, sowohl eine Referenz auf den Anfang als auch auf das Ende der Liste zu verwalten. 7.2 (Ü) Stack In dieser Aufgabe sollen Sie eine Klasse IntStack mit Hilfe einer verketteten Liste implementieren. Das einzelne Listenelement (einfache Integer-Werte) ist bereits als Klasse IntStack vorhanden. Überlegen Sie sich, welche Member-Variablen die Klasse Stack enthalten muss, damit Sie die geforderten Methoden implementieren können. Programmieren Sie anschließend die folgenden Funktionen: public void push(int i) public int pop() public boolean isempty() Testen Sie Ihre Implementierung an geeigneten Beispielen. 7.3 (Ü) Support Für den IT-Support soll ein Ticketsystem nach dem FIFO-Prinzip erstellt werden. Implementieren Sie hierfür eine Klasse Support, die neue Anfragen mit public void add(ticket ticket) anlegt. Mitarbeiter können mit der Methode public Ticket get() die älteste Anfrage abrufen. Erstellen Sie hierfür eine Klasse Ticket. Ein Ticket enthält einen Namen, eine Email Adresse und eine Anfrage. Die Ticket.toString()-Methode soll alle drei in geeigneter Form konkateniert als String zurückgegeben. Welche Datenstruktur bietet sich am ehesten für diese Problemstellung an? Verwenden Sie die auf Blatt 6 besprochene einfach verkette Liste als Basis für Ihre Lösung. 7.4 (Ü) Taschenrechner In dieser Aufgabe sollen Sie die bereits zuvor implementierte Klasse IntStack nutzen, um einen einfachen Taschenrechner zu programmieren. Da sich diese Klasse in einem anderen Package befindet, müssen Sie ein entsprechendes import-statement benutzen. Am einfachsten ist es anzunehmen, dass nur die Operanden +, und erlaubt sind und der Ausdruck in Postfix-Notation vorliegt, d.h. anstatt den Operator zwischen die beiden Operanden 2
zu schreiben (z.b. 42 + 8 ) schreibt man ihn nach den Operanden (also 42 8 + ). Entsprechend wird der Ausdruck 4 10 + 9 4 4 + 3 3 als 4 10 9 + 4 4 3 3 + geschrieben. Der Ausdruck wird ausgewertet, indem von links nach rechts die Zahlen und Rechenzeichen gelesen werden. Eine gelesene Zahl wird dabei auf den Stack gelegt. Operatoren werden auf die beiden obersten Zahlen im Stack angewendet und das Ergebnis wiederum auf den Stack gelegt. Nach der Auswertung des kompletten Ausdrucks liegt das Gesamtergebnis als einziger Wert auf dem Stack. Nachfolgend der Zustand des Stacks anhand des Beispiels 42 4 8 : 42 42 4 42 4 8 42 32 (Multiplikation von 4 und 8 ergibt 32) 10 (Subtraktion von 42 und 32 ergibt 10) Um den Ausdruck in seine Bestandteile zerlegen zu können, sollen sich zwischen allen Rechensymbolen und Zahlen Leerzeichen befinden. Durch Aufruf der Member-Funktion String[] split( ) aus der Klasse String können die Einzel-Ausdrücke dann in ein String-Array zerlegt werden. Hinweise: mit int compareto(string str) der Klasse String kann kontrolliert werden, ob zwei Zeichenketten identisch sind (Rückgabewert ist dann 0). mit int Integer.parseInt(String s) kann eine Zeichenkette, die eine Zahl darstellt, in die entsprechende Ganzzahl umgewandelt werden. Erstellen Sie nun eine Klasse Calculator, die eine Member-Variable vom Typ IntStack enthält. Fügen Sie anschließend die Funktion public int calculatepostfix(string expression) hinzu, die den in der Zeichenkette expression enthaltenen Ausdruck in Postfix-Notation auswertet. Siehe beiligende.java-files. 3
7.5 (H) Krankenhaus (+++) Entwickeln Sie eine Datenstruktur für die Verwaltung von Patienten in der Notaufnahme eines Krankenhauses. Erstellen Sie hierfür zunächst eine Klasse de.tum. ws2010. propra. blatt07. hospital. Patient die einen Namen und die Krankenkasse in Form einer Klasse de.tum. ws2010. propra. blatt07. hospital. HealthInsurance beinhaltet. Die Eigenschaften werden über den Konstruktor public Patient ( String name, HealthInsurance h) gesetzt. Über folgende Methoden kann auf die Eigenschaften zugegriffen werden: (a) public String getname() Gibt den Namen des Patienten zurück. (b) public HealthInsurance gethealthinsurance() Gibt die Krankenversicherung des Patienten zurück. Jede Krankenversicherung HealthInsurance hat einen Namen und eine Wahrscheinlichkeit, mit der Behandlungen am Patienten bezahlt werden. Diese Eigenschaften sind durch die folgenden Membermethoden zugänglich: (a) public HealthInsurance(String name, double paymentpractice) Dieser Konstruktor setzt den Namen der Versicherung und die Wahrscheinlichkeit zwischen 0 und 1, dass die Versicherung Behandlungen bezahlt, wobei 0 bedeutet, dass die Krankenkasse keine Behandlungen zahlt und 1, dass jede Behandlung bezahlt wird. (b) public String getname() Gibt den Namen der Krankenversicherung zurück. (c) public double getpaymentpractice() Gibt die Wahrscheinlichkeit zwischen 0 und 1 zurück, mit der die Versicherung Behandlungen bezahlt. Die Datenstruktur soll nun gewährleisten, dass Notfallpatienten vor allen anderen wartenden Patienten an die Reihe kommen, wobei grundsätzlich die Patienten in der Reihenfolge der Einlieferung behandelt werden. Für alle Übrigen gilt: Patienten mit einer Krankenkasse, die mit hoher Wahrscheinlichkeit Behandlungen bezahlt, kommen zuerst an die Reihe. Realisieren Sie dieses Verhalten in der Datenstruktur de.tum. ws2010. propra. blatt07. hospital. WaitingRoom Hierfür werden folgende Membermethoden verlangt: (a) public void add(patient p) Wird aufgerufen, wenn ein neuer Patient ankommt. (b) public void addemergency(patient p) Soll aufgerufen werden, wenn ein neuer Patient ankommt, bei dem ein Notfall vorliegt. 4
(c) public Patient get() Liefert den Patienten zurück, der als nächstes behandelt wird und löscht Ihn aus der Datenstruktur. (d) public String[] tostringarray() Gibt die Namen der Patienten im Wartezimmer und deren Krankenkassen mit Wahrscheinlichkeit in der korrekten Sortierung zurück. Der Patient, der als nächstes aufgerufen wird, steht am Anfang der Liste. Testen Sie Ihre Klassen mit geeigneten Eingaben. Hierzu ist es hilfreich, zu jeder Klasse eine geeignete tostring()-methode zu implementieren. Hinweis: Überlegen Sie sich zunächst, wie Sie bei den Notfällen vorgehen müssen. Die Datenstruktur für die übrigen Patienten ist ein wenig komplizierter. Denken Sie darüber nach, wie Sie die richtige Reihenfolge der Patienten gewährleisten können. Dies können Sie entweder beim Einfügen oder beim Herausnehmen aus dem WaitingRoom sicherstellen. 5