Übung: Algorithmen und Datenstrukturen SS 2007 Prof. Lengauer Sven Apel, Michael Claÿen, Christoph Zengler, Christof König Blatt 3 Votierung in der Woche vom 14.05.0718.05.07 Aufgabe 6 Kopieren von Arrays In Java sollen in einem Array Objekte gespeichert werden, die die Koordinaten eines Punktes in der Ebene enthalten: public class Point { public double x; public double y; public Point(double x, double y) { this.x = x; this.y = y; } } Überlegen Sie sich, wie Sie eine komplette Kopie (deep copy) des Arrays erzeugen können und welche Ansätze nicht funktionieren würden, z. B. für ein Array a soll b die Kopie sein: Point[] a = new Point[3]; a[0] = new Point(0,0); a[1] = new Point(1,1); a[2] = new Point(2,2); Point[] b; /* ... */ Lösung: 1. Referenz zuweisen: a und b zeigen auf dasselbe Array (Bild malen). b = a; 1 2. Referenzen der einzelnen Punkte zuweisen: a und b zeigen auf verschiedene Arrays. Allerdings zeigen a[0] und b[0] immer noch auf dasselbe PointObjekt (Indizes 1 und 2 genauso). b = new Point[3]; for(int i = 0; i < b.length; b++) b[i] = a[i]; 3. Methode clone aufrufen: siehe 2 (auch wenn Point vom Typ Cloneable ist) Point[] b = a.clone(); 4. Systemfunktion nutzen: siehe 2. b = new Point[3]; System.arraycopy(a, 0, b, 0, a.length); 5. Array and einzelne Point-Objekte jeweils neu anlegen: erzeugt eine komplette Kopie. b = new Point[3]; for(int i = 0; i < b.length; b++) { b[i] = new Point(a[i].x, a[i].y); Aufgabe 7 Arrays und Listen Aufgabe ist es, eine Datenstruktur Buffer mit folgenden Operationen zu implementieren: • void insert(int i, Element e) fügt an der i-ten Position ein Element in den Puer ein, ohne dass dabei andere Elemente überschrieben werden. Es ist darauf zu achten, nicht über das bisherige Ende des Puers hinauszuschreiben. (Tipp: Verwenden Sie im Falle der Array-basierten Implementierung eine Variable size, die für die aktuelle Gröÿe des Puers steht.) • void push_back(Element e) fügt ein Element an das Ende des Puers an, ohne dass dabei andere Elemente überschrieben werden. • void push_front(Element e) fügt ein Element vor dem bisher ersten Element des Puers ein, ohne dass dabei andere Elemente überschrieben werden. Verwenden Sie als interne Speicherstruktur (a) ein Array und (b) eine einfach verkettete Liste. Geben sie jeweils an, welche Zeitkomplexität die einzelnen Operationen haben. (Tipp: Wieviele elementare Rechenschritte benötigt die Ausführung einer Operation ungefähr?) Lösung: Implementierung basierend auf einem Array: 2 class ArrayBuffer { Element[] buffer = new Element[42]; int size = 0; void insert(int i, Element e) { // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // ca. O(n) Verschiebe-Operationen // Puffer Vergroessern wird von Verschiebe-Operationen dominiert // Element tmpElement; if(buffer.length <= size+1) { resizeBuffer(); } int pos = i; Element currElement = buffer[pos]; Element nextElement; buffer[pos] = e; } while (currElement != null) { nextElement = buffer[pos+1]; pos++; buffer[pos] = currElement; currElement = nextElement; } size++; void push_back(Element e) { } // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // hier ueberwiegt das Vergroessern des Puffers, // weil beim Einfuegen am Ende des Puffers keine Elemente verschoben // werden muessen: -> O(log n) // (Verdoppeln nur in logarithmisch vielen Schritten) // if(buffer.length <= size+1) { resizeBuffer(); } insert(size, e); size++; void push_front(Element e) { 3 } // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // ca. O(n) Verschiebe-Operationen // Puffer Vergroessern wird von Verschiebe-Operationen dominiert // if(buffer.length <= size+1) { resizeBuffer(); } insert(0, e); size++; private void resizeBuffer() { Element[] newBuffer = new Element[buffer.length * 2]; for (int pos = 0; pos < buffer.length; pos++) { newBuffer[pos] = buffer[pos]; } } buffer = newBuffer; } Lösung: Implementierung basierend auf einer Liste: class ListBuffer { ElementList buffer = new ElementList(); void insert(int i, Element e) { // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // ca. O(n) Weitersetzen des Zeigers in der linked list // Node currNode = buffer.getHead(); Node newNode = new Node(e); if (i == 0) { // fuege neues Element am Kopf der Liste ein: newNode.setNext(currNode); buffer.setHead(newNode); } else { for (int pos=0; pos < i-1; pos++) { currNode = currNode.getNext(); 4 } } } Node tmpNode = currNode.getNext(); currNode.setNext(newNode); newNode.setNext(tmpNode); void push_back(Element e) { } // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // ca. O(n) Weitersetzen des Zeigers in der linked list // (der Unterschied zur insert Operation kann hier // naeherungsweise vernachlaessigt werden) // // Alternative (sehr relevant in der Praxis!): // Verwendung eines Zeigers fuer das Ende der Liste // -> O(1) push_back moeglich! Node currNode = buffer.getHead(); for (int pos=0; pos < buffer.size(); pos++) { currNode = currNode.getNext(); } currNode.setNext(new Node(e)); void push_front(Element e) { } // Komplexitaet pro eingefuegtes Element // bei Puffer der Laenge n: // ca. O(n) Weitersetzen des Zeigers in der linked list // insert(0, e); } Aufgabe 8 Listen durchlaufen Stellen Sie sich vor, Sie haben eine Java-Implementierung einer Notizliste gegeben: LinkedList<String> notes = new LinkedList<String>(); notes.add("note 1"); notes.add("note 2"); notes.add("note 3"); notes.add("note 4"); 5 /* ... */ Überlegen Sie sich, wieviele verschiedene sinnvolle Möglichkeiten existieren, die Liste in Java zu durchlaufen. Zeigen Sie, wie eine solche Liste unter Verwendung der unten stehenden Mechanismen durchlaufen werden kann: (a) for-Schleife (for-each-Schleife) (b) while-Schleife und Zugri über Index (c) while-Schleife und Zugri über Iterator Lösung: (a) for-Schleife (for-each-Schleife) for(String note : notes) { System.out.println(note); } (b) while-Schleife und Zugri über Index int i = 0; while(i < notes.size()) { System.out.println(notes.get(i++)); } (c) while-Schleife und Zugri über Iterator Iterator<String> it = notes.iterator(); while(it.hasNext()) System.out.println(it.next()); 6