Informatik I WS 05/06 Prof. Dr. W. May Dipl.-Inform. Oliver Fritzen Dipl.-Inform. Christian Kubczak Übungsblatt 10 Ausgegeben am: Abgabe bis: 13.01.2006 24.1.2006 (Theorie) 27.1.2006 (Praktisch) Thema: Abstrakte Datentypen, Datenstrukturen in Java Die Praxis-Aufgaben auf diesem Übungsblatt bearbeiten Sie bitte am Rechner und führen Sie Ihrem Tutor vor. Die Theorie-Aufgaben bearbeiten Sie bitte und werfen die en zum oben genannten Termin in die Zettelkästen im Erdgeschoss der NAM. Eine Bearbeitung in Zweier-Teams innerhalb Ihrer Übungsgruppe ist möglich. Nutzen Sie zum Testieren auch das Angebot der abendlichen Freien Übungen. Aufgabe 1 (Theorie: 25 Punkte): Abstrakter Datentyp Liste<Nat> Eine Liste von natürlichen Zahlen ist darstellbar als abstrakten Datentyp Liste<Nat> über den Typ Nat. Liste<T> und Nat sind aus der Vorlesung bekannt. 1. Geben Sie Signatur und Axiome für einen Operator "rev" an, der die Reihenfolge der Liste "umdreht". Z.B. gilt: rev [9,0,1,2,5] = [5,2,1,0,9]. 2. Geben Sie Axiome für einen Operator rip an, der aus einer Liste jedes zweite Element entfernt. Wie müssten die Axiome lauten wenn statt dem 2.,4.,6. Element etc. das 1.,3.,5. Element etc. entfernt werden soll? 3. Geben Sie Signatur und Axiome für einen Operator sum an, der die einzelnen Listenelemente aufsummiert und das Ergebnis zurück liefert. 4. Definieren Sie Axiome für einen fold-operator: fold: Liste<T> Op T T T T T. Op T T T steht dabei für die Menge aller zweistelligen, einwertigen Operationen über T, also Operationen die zwei Elemente des Typs T auf ein Element des Typs T abbilden. Formal: Op T T T = {op op:t T T. Beispiel: plus: Nat Nat Nat ist für T = Nat ein solcher Operator. "fold" soll folgendes tun: fold(l,op,n) = op(n,op(l 1,op(l 2,op(l 3,op(l 4,...))))) für eine nichtleere Liste L=[l 1, l 2, l 3,...l n ]. für eine leere Liste sei fold( create, op,e) = e. Was berechnet fold([4,7,1,1], plus, null)? 1. rev: Liste<T> Liste<T> (kann allgemein für Listen definiert werden) rev(create) = create rev(add(e,l)) = append(e, rev(l))
2. rip: Liste<T> Liste<T> (kann allgemein für Listen definiert werden) rip(create) = create rip(add(y,add(x,l)) = add(y,rip(l)) Die andere Alternative würde lauten: rip(create) = create rip(add(y,add(x,l)) = add(x,rip(l)) 3. sum: Liste<Nat> Nat sum(create) = null sum(add(n,l)) = plus(n, sum(l)) 4. fold(create,op, e) = e fold(add(e L,L), op, e start ) = op(e start, fold(l, op, e L )). Da fold(l,plus,null) die Summe aller Listenelemente berechnet, ist das Ergebnis von fold([4,7,1,1],+,0) = 4+7+1+1=13, bzw. als Term ausgeschrieben: succ(succ(succ(succ(succ(succ(succ(succ(succ(succ(succ(succ(succ(null))))))))))))). (10 Punkte) Aufgabe 2 (Theorie: 25 Punkte): Abstrakter Datentyp Queue Aus der Vorlesung kennen Sie den abstrakten Datentyp Queue<T> von Elementen des Typs T: Operatoren: create: Queue<T> enq: (Queue<T>,T) Queue<T> deq: Queue<T> Queue<T> first: Queue<T> T is_empty: Queue<T> Bool Axiome: (A) deq(enq(create,x)) = create (B) deq(enq(enq(q,y),x)) = enq(deq(enq(q,y)),x) (C) first(enq(create,x)) = x (D) first(enq(enq(q,x),y)) = first(enq(q,x)) (E) is_empty(create) = true (F) is_empty(enq(q,x)) = false Berechnen Sie die Normalform von: 1. first(enq(enq(create,1),2)) 2. deq(enq(enq(enq(create,1),2),3)) 3. is_empty(deq(enq(deq(enq(enq(create,1),2)),3))) 4. enq(enq(enq(create,1),2), first(enq(enq(create,3),4))) 1. first(enq(enq(create,1),2)) = (D) first(enq(create,1)) = (C) 1. (3 Punkte) 2.
deq(enq(enq(enq(create,1),2),3)) = (B) enq(deq(enq(enq(create,1),2)),3) = (B) enq(enq(deq(enq(create,1)),2),3) = (A) enq(enq(create,2),3). 3. (6 Punkte) is_empty(deq(enq(deq(enq(enq(create,1),2)),3))) = (B) is_empty(deq(enq(enq(deq(enq(create,1)),2),3))) = (B) is_empty(enq(deq(enq(deq(enq(create,1)),2)),3)) = (F) false. 4. (8 Punkte) enq(enq(enq(create,1),2), first(enq(enq(create,3),4))) = (D) enq(enq(enq(create,1),2), first(enq(create,3))) = (E) enq(enq(enq(create,1),2),3). (8 Punkte) Es ist hilfreich, sich die Queue-Operationen dazu aufzumalen: Ein enqeue(q,x) entspricht einem Anhängen von Element x an die Warteschlange q ("wer neu ist, hinten Anstellen!"). Ein dequeue(q) entspricht dem Wegnehmen des vordersten Elementes aus der Schlange q ("der nächste bitte!"). Ein first(q) liefert das vorderste Element aus der Schlange q ("Wer kommt als nächster dran?"). Ein is_empty(q) gibt an, ob die Warteschlange q leer ist oder Elemente enthält. Zeitliche Reihenfolge: Die "innersten" Aufrufe (also diejenigen, die am tiefsten geschachtelt sind), sind als erste passiert, die äußersten zuletzt. Aufgabe 3 (Theorie: 15 Punkte): Normalform für Queue-Terme Gegeben sei der ADT Queue für FIFO-Warteschlangen aus der Vorlesung. Bringen Sie folgende Terme auf eine Normalform: 1. deq(enq(enq(create,1),2)) 2. is_empty(deq(enq(deq(enq(create,1)),2))) 3. first(enq(deq(enq(create,7)),3)) 1. deq(enq(enq(create,1),2)) = enq(create,2) (3 Punkte) 2. is_empty(deq(enq(deq(enq(create,1)),2))) = true (6 Punkte) 3. first(enq(deq(enq(create,7)),3)) = 3 (6 Punkte)
Aufgabe 4 (Praktisch: 35 Punkte): InsertionSort für Java-Listen Betrachten Sie den Java-Typ Liste aus der Vorlesung, der eine einfach verkettete Liste in Java implementiert. 1. Schreiben sie eine Klasse InsertionSort mit einer entsprechenden Methode, die eine solche Liste als Eingabe nimmt, sie mittels InsertionSort sortiert und das Ergebnis als Liste zurück liefert. Hierzu soll erst einmal kontrolliert werden ob alle head-elemente in der übergebenen Liste vom Typ Comparable sind. Dann soll die Liste aufsteigend mit Hilfe von compareto sortiert werden. 2. Modifizieren sie die Testklasse ListeTest entsprechend um die Sortierfunktionalität zu testen. 1. alle Methoden sollten static sein (2 Punkte), und nur die Hauptmethode (hier InsertionSort(Liste current)) sollte public sein (2 Punkte). Die Hauptmethode muss eine Liste als Ergebnistyp zurück liefern, da call by reference hier nicht funktioniert (3 Punkte). Auf Comparable überprüfen (3 Punkte)! public class InsertionSort { public static Liste insertionsort(liste current) { // all head objects must implement "Comparable" if (checkcomparable(current) == false) { System.err.println("Cannot sort Liste: "+ "Object "+current.the_head+" is not Comparable!"); return null; return insertionsort(current, new Liste()); private static Liste insertionsort(liste unsorted, Liste sorted) { // System.out.println("unsorted: " + unsorted + ", sorted: " + sorted); // we are done: if (unsorted.is_empty()) return sorted; // chop off first element from "unsorted" list: Liste first = unsorted; unsorted = unsorted.the_tail; Comparable fhead = (Comparable) first.the_head; // special case: "first" is also first of (possibly empty) list "sorted" if(sorted.is_empty() fhead.compareto(sorted.the_head) < 0) { first.the_tail = sorted; return insertionsort(unsorted, first); Liste current = sorted; Liste currentold = null; while(! current.is_empty() && fhead.compareto(current.the_head) > 0) { // while "fhead" is greater than the current head, // move "current" and "currentold" one element further
currentold = current; current = current.the_tail; // insert "first" into the "sorted" list. currentold.the_tail = first; first.the_tail = current; return insertionsort(unsorted, sorted); private static boolean checkcomparable(liste l) { if (l.is_empty()) return true; return (l.the_head instanceof Comparable) && checkcomparable(l.the_tail); Code: (20 Punkte) 2. Testklasse ListeTest: public class ListeTest { public static void main(string[] args) { [...] // wie gehabt my_liste = new Liste(); my_liste = new Liste().add(3).add(1).add(2); my_liste = new Liste().add(3).add(1).add(2).add(4); my_liste = new Liste().add(5).add(4).add(3).add(2); my_liste = new Liste().add(5).add(4).add(3).add(2).add(1).add(5).add(0).add(1); oder ähnlich.