Ordnung im Materiallager: Datenstrukturen II Suchen und Sortieren im Array Verkettete Listen Rekursion
Indizierter Datenbehälter Modell: Parkhaus, nummerierte Plätze interface FuhrparkIndex { // indiziert public int hinzufuegen(fahrzeug fz); // liefert Index public int suchen(fahrzeug fz); // Vergleichsobjekt public Fahrzeug lesen(int index); public void loeschen(int index); public int anzahl(); (c)schmiedecke 04 Pr11-Datenstrukturen II 2
Implementierung mit einem Array class FuhrparkArray implements FuhrparkIndex { // private Attribute private Fahrzeug[] fahrz; private int anzahl = 0; // Standardkonstruktor, feste Groesse public FuhrparkArray() { fahrz = new Fahrzeug[50]; // Konstruktor mit Groessenangabe public FuhrparkArray(int groesse) { fahrz = new Fahrzeug[groesse]; (c)schmiedecke 04 Pr11-Datenstrukturen II 3
// Hinzufügen, sofern Platz ist public int hinzufuegen(fahrzeug fz) { if (anzahl >= fahrz.length) throw new VollException(anzahl); fahrz[anzahl] = fz; anzahl++; return anzahl-1; // hinzufügen public class VollException extends IndexOutOfBoundsException { public int anzahl() { return anzahl; // anzahl public class IndexException extends IndexOutOfBoundsException { // lesen, falls vorhanden public Fahrzeug lesen(int index) { if (index < 0 index >= fahrz.length) throw new IndexException(index); if (index >= anzahl) return null; // oder IndexException... return fahrz[index]; // lesen (c)schmiedecke 04 Pr11-Datenstrukturen II 4
public int suchen(fahrzeug fz) // Vergleichsobjekt throws NoObjectException { if (fz==null) throw new NoObjectException(); for (int i=0; i<anzahl; i++) if (fz[i].equals(fz)) return i; // gefunden return -1; // nicht gefunden // löschen und zusammenschieben public void loeschen(int index) { if (index <0 index >= anzahl) throw new IndexException(index); for (int i=index; i<anzahl-1; i++) fahrz[i] = fahrz[i+1]; // hochschieben fahrz[anzahl-1] = null; // ehem. letztes löschen anzahl --; // Fertig, Klassenende class NoObjectException extends Exception { (c)schmiedecke 04 Pr11-Datenstrukturen II 5
Sortieren im Array Die Klasse FuhrparkArray wird um die Methode sortieren() erweitert. Voraussetzung: Fahrzeug muss eine Methode groesser() enthalten. Hinweis: nach dem Sortieren sind alle bis dahin gelieferten Indizes ungültig! (c)schmiedecke 04 Pr11-Datenstrukturen II 6
Sortieren public int sortieren() { // "Bubblesort" // Idee: "schwere" Elemente sinken nach unten for (int i=anzahl-1; i>0; i--) { // rueckwärts Fahrzeug letztes = fahrz[i]; for (int k=i-1; k>=0; k--) { Fahrzeug vergleich = fahrz[k]; // vorgänger if (vergleich.groesser(letztes)) { //vertauschen: fahrz[i] = letztes; fahrz[k] = vergleich; letztes = vergleich; //neuer Vergleichswert // for k // for i // sortieren Invariante: Die letzen Einträge sind sortiert und größer als alle anderen. Umformulieren: leichte Elemente steigen auf... (c)schmiedecke 04 Pr11-Datenstrukturen II 7
Suchen im sortierten Array Der effizienteste Suchalgorithmus ist die Binäre Suche (BinSearch) Sie funktioniert nur im dichten Array (keine Lücken) Die Idee ist einfach: es wird jeweils ermittelt, in welcher Hälfte des Arrays der gesuchte Wert liegen muss. (c)schmiedecke 04 Pr11-Datenstrukturen II 8
Binäre Suche public int suchen(fahrzeug fz) {//"Binsearch" // Idee: Mitten ins Suchintervall greifen int min = 0, max = anzahl - 1; while (min <= max) { int mitte = (min + max) / 2; Fahrzeug fzm = fahrz[mitte]; if (fzm.groeser(fz)) min = mitte + 1; // hinten suchen else if (fz.eq(fzm)) return mitte; // gefunden else max = mitte - 1; // vorne suchen // while return 1; // nicht gefunden // suchen Invariante: Das gesuchte Element befindet sich zwischen min und max. (c)schmiedecke 04 Pr11-Datenstrukturen II 9
Alternative zu Arrays: Verkettete Listen Element Element class Node { // Knoten Element element; Node next; Element rekursive Definition ø (c)schmiedecke 04 Pr11-Datenstrukturen II 10
Einfügen Element Element Element Element ø void einfuegennach(knoten kn, Element el) { kn.naechster = new Knoten(el, kn.naechster); (c)schmiedecke 04 Pr11-Datenstrukturen II 11
Löschen Element Element Element ø void loeschennach(knoten kn) throws LeerException { try { kn.naechster = kn.naechster.naechster; catch (NullPointerException ex) { throw new LeerException("nix zu loeschen"); (c)schmiedecke 04 Pr11-Datenstrukturen II 12
Stack und Queue (Stapel und Warteschlange) Zwei sehr wichtige Datenbehälter-Typen Identische Schnittstelle, unterschiedliches Verhalten interface Container { void add(object o); Object remove(); interface LiFo extends Container { interface FiFo extends Container { (c)schmiedecke 04 Pr11-Datenstrukturen II 13
Array als LiFo class StackArray implements LiFo { private Object[] stack = new Object[size]; private int top = 0; public void add(object o) throws OverflowExc { if (top == size) throw new OverflowExc(); stack[top] = o; top++; public Object remove() throws UndeflowExc { if (top==0) throw new UndeflowExc(); top --; return stack[top+1]; (c)schmiedecke 04 Pr11-Datenstrukturen II 14
Liste Als LiFo stack class ListArray implements LiFo { private Node stack; public void add(object o) { if (stack==null) stack = new Node(o,null); else stack = new Node(o,stack); public Object remove() throws UndeflowExc { if (stack==null) throw new UndeflowExc(); Object o = stack.element; stack = stack.next; return o; (c)schmiedecke 04 Pr11-Datenstrukturen II 15
Array als FiFo class QueueArray implements FiFo { private Object[] queue = new Object[size]; private int first=0, count=0; last=0; void add(object o) throws OverflowExc { if (count>=size) throw new OverflowExc(); queue[last] = o; last = (last+1)%size; // modulo count ++; Object remove() throws UnderflowExc { if (count==0) throw new UnderflowExc(); Object o = queue[first]; count --; first = (first+1)%size; return o; next 11 10 9 (c)schmiedecke 04 Pr11-Datenstrukturen II 16 8 12 7 0 6 1 2 3 4 5
Liste als FiFo first last class ListQueue implements FiFo { private Node first=null, last=null; public void add(object o) { Node newnode = new Node(o, null); if (last==null) first = last = newnode; else last = last.next = newnode; public Object remove() throws UndeflowExc { if (first==null) throw new UndeflowExc(); Object o = first.element; first = first.next; if (first==null) last = null; return o; (c)schmiedecke 04 Pr11-Datenstrukturen II 17
Suchen in einer verketteten Liste Container, die durchsuchbar sind, sollen das Interface Searchable implementieren: interface Searchable { boolean contains(object o) first last (c)schmiedecke 04 Pr11-Datenstrukturen II 18
Iterative Lösung iterativ: schrittweise In der Programmierung bedeutet das "unter Verwendung einer Schleife" wo kann die NullPointer Exc. auftreten? public class LiFoSearchable extends LiFoList implements Searchable { public boolean contains(object o) { Node searchpt = first; try { while (true) { if (searchpt.element.equals(o)) return true; searchpt = searchpt.next; catch (NullPointer Exception ex) { return false; return false; // nur formal, wird nie erreicht. (c)schmiedecke 04 Pr11-Datenstrukturen II 19
Rekursive Lösung rekursiv: mit Bezug auf sich selbst. In der Programmierung bedeutet das, dass eine Methode sich selbst aufruft. Die Bedingungen müssen sich ändern (Parameter, Objektzustand), sonst kommt es zur Endlos-Rekursion! public class LiFoSearchableRek extends LiFoList implements Searchable { public boolean contains(object o) { return contains(first,o); Rekursion private boolean contains(node list, Object o) { try { if (list.element.equals(o)) return true; else return contains(list.next, o); catch (NullPointer Exception ex) { return false; (c)schmiedecke 04 Pr11-Datenstrukturen II 20
FiFo ohne last-zeiger public void add(object o) { Node newnode = new Node(o, null); if (first == null) first = newnode; else add(first, newnode); private add(node list, Node new){ try { add(list.next, new); catch (NullPointerException ex) { list.next = new; first last (c)schmiedecke 04 Pr11-Datenstrukturen II 21
Sortiert einfügen public void add(object o) { if (first == null) first = new Node(o,null); else first = add(first, o); // Methode gibt Liste mit Neueintrag zurück private Node add(node list, Node new){ if (list.element.greater(o) return new Node(o, list); // vorn einfügen, Ende! try { list.next = add(list.next, o); // in Rest einf. catch (NullPointerException ex) // gibt kein größeres { list.next = new Node (o,null); // hinten anhängen return list; (c)schmiedecke 04 Pr11-Datenstrukturen II 22
Array oder verkettete Liste?? Vorteile Array: Suchen und Sortieren einfach Indizierte Zugriff einfach (i-tes Element) Stack und WS einfach Vorteile verkettete Liste: keine Längenbeschränkung Speicherplatzbelegung nach Bedarf Mittiges Einfügen und Entnehmen ohne Verschieben Stack und WS einfach Wahl der Implementierung nach im Einzelfall entscheidet über Effizienz Implementierung von Interfaces erleichtert den Austausch (c)schmiedecke 04 Pr11-Datenstrukturen II 23
Fertige Datenbehälter Datenbehälter braucht man immer Es gibt eine große gemeinsame Schnittstelle aller Datenbehälter Auch die spezialisierten Schnittstellen und Verhalten sind standardisiert Die optimale Implementierung richtet sich nach dem Verwendungszweck Die Effizienz und Zuverlässigkeit hängt von der Implementierung ab Alles spricht für Fertigprodukte! (c)schmiedecke 04 Pr11-Datenstrukturen II 24
..aber davon reden wir das nächste Mal: Das Java-Collections-Famework. Nie mehr Datenbehälter selbst zimmern!