Datenstrukturen in Java Datenstrukturen in Java Gliederung Algorithmen und Datenstrukturen II 1 Datenstrukturen in Java Datenstrukturen in Java Listen Node LinkedList Abstraktion D. Rösner Stack Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg Interface Implementierung LinkedStack Beispiel c Sommer 2009, 4. Mai 2009, 2009 D.Rösner D. Rösner AuD II 2009 . . . Datenstrukturen in Java D. Rösner AuD II 2009 . . . 1 Listen Stack Datenstrukturen in Java Java: Listen 2 Listen Stack Java: Listen Klasse für Knoten in Java public class Node { einfach verkettete Listen (engl. singly linked lists) // Instance variables: Grundbaustein: Knoten, der Referenz auf ein Element und Referenz auf den Folgeknoten speichert lineare Ordnung der Elemente Ende der Liste, wenn Folgeknoten-Verweis auf null private Object element; // beliebige Objekte als Elemente s.a. [GT01] bzw. [SS02] private Node next; // Folgeknoten ... D. Rösner AuD II 2009 . . . 4 D. Rösner AuD II 2009 . . . 5 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Listen Listen Stack Java: Listen ... // Selektoren Object getElement() { return element; } Node getNext() { return next; } Klasse für Knoten cont. ... // Konstruktoren public Node() { this(null, null); } // Modifikatoren void setElement(Object newElem) { element = newElem; } void setNext(Node newNext) { next = newNext; } public Node(Object e, Node n) { element = e; next = n; } ... D. Rösner AuD II 2009 . . . Datenstrukturen in Java 6 } D. Rösner AuD II 2009 . . . Listen Stack Datenstrukturen in Java Java: Listen 7 Listen Stack Java: Listen Klasse LinkedList public class LinkedList { // Instance variables: Operation: Einfügen eines neuen Knoten am Kopf einer Liste Ablauf: // Zeiger auf Listenbeginn private Node head; kreiere neuen Knoten dessen next zeige auf das von head referenzierte Objekt head zeige auf neuen Knoten // Laenge der Liste private int length; Zeitbedarf konstant, d.h. O(1) // optional: Zeiger auf letztes // Element private Node last; ... D. Rösner AuD II 2009 . . . 8 D. Rösner AuD II 2009 . . . 9 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Listen Listen Stack Java: Listen Operationen am Ende einer Liste Bemerkungen zu Listen Einfügen eines neuen Elements: wenn Referenz auf letztes Listenelement verfügbar, analog zum Einfügen am Kopf in konstanter Zeit Entfernen: selbst wenn Referenz auf letztes Listenelement verfügbar, ist der letzte Knoten in einer einfach verketteten liste nicht in konstanter Zeit entfernbar, da der vorletzte Knoten nur ausgehend vom Listenbeginn erreicht werden kann D. Rösner AuD II 2009 . . . Datenstrukturen in Java rekursiv definierte Datenstruktur: eine Liste ist entweder leer oder sie hat ein erstes Element und einen Rest, der Liste ist damit: viele Operationen auf Listen rekursiv Listenelemente können selbst Listen sein (verschachtelte Listen) durch entsprechende Verkettung von Knoten lassen sich auch zirkuläre Listen kreieren D. Rösner AuD II 2009 . . . 10 Listen Stack Datenstrukturen in Java Java: Algorithmen auf Listen Listen Stack Java: Listen Abstraktion: einer Liste wird ein Wert zugewiesen Aufbau: die induktive Definition des ADT Liste spiegelt sich in vielen Algorithmen auf Listen direkt wieder viele Algorithmen folgen dabei abstrakten Mustern z.B. Muster: Rekursion mit Restliste (tail recursion) vgl. rekursive Berechnung der Länge einer Liste if (isEmpty(list)) return <Wert-fuer-leere-Liste>; else return <Wert-fuer-erstes-Element> <verknuepft-mit> <Wert-fuer-Rest-der-Liste>; public static int length(LinkedList list) { if (isEmpty(list)) return 0; else return 1 + length(rest(list)); } Variante: if (isSingleton(list)) return <Wert-fuer-einelementige-Liste>; else return <Wert-fuer-erstes-Element> <verknuepft-mit> <Wert-fuer-Rest-der-Liste>; Bem: O(n); effizienter mit Verwendung und Fortschreibung einer Instanzvariable size D. Rösner AuD II 2009 . . . 11 12 D. Rösner AuD II 2009 . . . 13 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Listen Listen Stack Java: Listen Beispiele für Abstraktion: einer Liste wird ein Wert zugewiesen Bestimmung des Produkts der Werte in einer Zahlenliste Bestimmung der Summe der Werte in einer Zahlenliste public static int listProd(LinkedList list) { // list enthalte nur Zahlen if (isEmpty(list)) return 1; else return firstElem(list) * listProd(rest(list)); } public static int listSum(LinkedList list) { // list enthalte nur Zahlen if (isEmpty(list)) return 0; else return firstElem(list) + listSum(rest(list)); } D. Rösner AuD II 2009 . . . Datenstrukturen in Java 14 D. Rösner AuD II 2009 . . . Listen Stack Datenstrukturen in Java Java: Listen 15 Listen Stack Java: Listen Prädikate auf Listen über Prädikate auf Elementen Beispiele für Abstraktion cont. Muster: Prädikate auf Listen, die dann true, wenn ein Elementprädikat für alle Listenelemente zutrifft Bestimmung des grössten Werts in einer Zahlenliste public static int listMax(LinkedList list) { // list enthalte nur Zahlen if (isSingleton(list)) return firstElem(list); else return max(firstElem(list), listMax(rest(list))); } D. Rösner AuD II 2009 . . . Muster: if (isEmpty(list)) return true; else return (<elemPraed>(firstElem(list)) && <listPraed>(rest(list))); 16 D. Rösner AuD II 2009 . . . 17 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Listen Listen Stack Java: Listen Muster: Filtern einer Liste mit einem Prädikat Prädikate auf Listen cont. ein Prädikat wird auf die Elemente einer Liste angewendet Muster: Prädikate auf Listen, die dann true, wenn ein Element existiert, auf das ein Elementprädikat zutrifft Ergebnis ist eine Liste, in der nur noch die Elemente enthalten sind, auf die das Prädikat zutrifft Muster: if (isEmpty(list)) return list; else if (<Praedikat>(firstElem(list))) return new Linkedlist(firstElem(list), <Filter>(rest(list))); else <Filter>(rest(list)); if (isEmpty(list)) return false; else return (<elemPraed>(firstElem(list)) || <listPraed>(rest(list))); D. Rösner AuD II 2009 . . . Datenstrukturen in Java D. Rösner AuD II 2009 . . . 18 Listen Stack Datenstrukturen in Java Java: Stack 19 Listen Stack Java: Stack abstrakter Datentyp Stack abstrakter Datentyp (ADT): spezifiziert durch Angabe von LIFO-Prinzip: last-in, first-out zwei zentrale Operationen: push(obj) Input: obj; Output: void füge obj als neues oberstes Element in den Stack ein pop() Input: (); Output: obj entferne das aktuelle oberste Element vom Stack und gib es als Wert zurück; Fehler, falls Stack leer Konstruktor(en) Selektor(en) Prädikat(en) Modifikator(en) in Java: Spezifikation eines ADT durch Definition eines Interface D. Rösner AuD II 2009 . . . 21 D. Rösner AuD II 2009 . . . 22 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Interface für Stack (vgl. [GT01], Ch. 4.1]) Interface für Stack cont. public interface Stack { ... // Hilfsmethoden // Kernmethoden /** * @return number of elements in the stack. */ public int size(); /** * Insert an element at the top of the stack. */ public void push (Object element); /** * @return true if the stack is empty, * false otherwise. */ public boolean isEmpty(); /** * Remove the top element from the stack. * @return element removed. */ public Object pop() throws StackEmptyException; ... D. Rösner AuD II 2009 . . . Datenstrukturen in Java 23 D. Rösner AuD II 2009 . . . Listen Stack Datenstrukturen in Java Java: Stack 24 Listen Stack Java: Stack Implementierung des ADT Stack mit konkretem Datentyp Array (vgl. [GT01], Ch. 4.1) public class ArrayStack implements Stack { /** * Default length of array used to implement the stack. */ public static final int CAPACITY = 1000; /** * Lenght of the array used to implement the stack. */ private int capacity; /** * Array used to implement the stack. */ private Object S[]; /** * Index of the top element of the stack in the array. */ private int top = -1; ... Interface für Stack cont. /** * @return top element in the stack. * @exception StackEmptyException if the stack is empty. */ public Object top() throws StackEmptyException; } D. Rösner AuD II 2009 . . . 25 D. Rösner AuD II 2009 . . . 26 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Implementierung mit Array cont. ... /** * Initialize the stack to use an array * of default length CAPACITY. */ public ArrayStack() { this(CAPACITY); } ... /** * Initialize the stack to use an array * of given length. * @param cap length of the array. */ public ArrayStack(int cap) { capacity = cap; S = new Object[capacity]; } ... D. Rösner AuD II 2009 . . . Datenstrukturen in Java Implementierung mit Array cont. ... /** * O(1) time. */ public int size() { return (top + 1); } ... /** * O(1) time. */ public boolean isEmpty() { return (top < 0); } 27 D. Rösner AuD II 2009 . . . Listen Stack Datenstrukturen in Java Java: Stack 28 Listen Stack Java: Stack Implementierung mit Array cont. Implementierung mit Array cont. /** * O(1) time. * @exception StackFullException if the array is full. */ public void push(Object obj) throws StackFullException { if (size() == capacity) throw new StackFullException("Stack overflow."); S[++top] = obj; } ... D. Rösner AuD II 2009 . . . ... /** * O(1) time. */ public Object top() throws StackEmptyException { if (isEmpty()) throw new StackEmptyException("Stack is empty."); return S[top]; } 29 D. Rösner AuD II 2009 . . . 30 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Implementierung mit Array cont. Stacks werden in vielen Situationen verwendet Beispiele: /** * O(1) time. */ public Object pop() throws StackEmptyException { Object elem; if (isEmpty()) throw new StackEmptyException("Stack is Empty."); elem = S[top]; S[top--] = null; // dereference S[top] for garbage collection. return elem; } } D. Rösner AuD II 2009 . . . Datenstrukturen in Java WWW-Browser: Stack der zuletzt besuchten Seiten; verwendet für Operation Back Editoren: Stack der zuletzt ausgeführten Befehle; verwendet für Operation Undo bei der Programmabarbeitung: Stack der offenen Methodenaufrufe Operandenstack beim Auswerten arithmetischer Ausdrücke u.v.a.m. D. Rösner AuD II 2009 . . . 31 Listen Stack Datenstrukturen in Java Java: Stack 32 Listen Stack Java: Stack Beispiel: Verwendung eines Stack zum ‘Umdrehen’ eines Array (vgl. [GT01], Ch. 4.1) vordefinierte Klasse Stack in Java Klasse java.util.Stack speichert beliebige Java-Objekte Kernmethoden: push(obj) , pop() Hilfsmethoden: peek() (entspricht: top() ) size(), empty() (entspricht: isEmpty() ) peek() und pop() werfen bei leerem Stack die StackEmptyException public static Integer[] reverse(Integer[] a) { ArrayStack S = new ArrayStack(a.length); Integer[] b = new Integer[a.length]; for (int i=0; i < a.length; i++) S.push(a[i]); for (int i=0; i < a.length; i++) b[i] = (Integer) (S.pop()); // Beachte: Casting erforderlich ! return b; } D. Rösner AuD II 2009 . . . 33 D. Rösner AuD II 2009 . . . 34 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Stack als Liste public class LinkedStack implements Stack { // Instanzvariable private Node top; // oberstes Element private int size; // Groesse des Stack Stack mit einer einfach verketteten Liste implementieren im Prinzip könnte Einfügen/Entfernen am Listenanfang oder am Listenende erfolgen Einfügen/Entfernen am Listenanfang ist aber O(1) für beide Operationen Speicherbedarf ist O(n) mit n Anzahl der Listenelemente beachte: bei der folgenden Implementierung kann Problem des Stack-Überlauf auf Laufzeitsystem verlagert werden (OutOfMemoryError) D. Rösner AuD II 2009 . . . Datenstrukturen in Java 35 // Konstruktor public LinkedStack() { top = null; size = 0; } // Selektor fuer Groesse public int size() { return size; } // Praedikat public boolean isEmpty() { if (top == null) return true; return false; } D. Rösner AuD II 2009 . . . ... Listen Stack Datenstrukturen in Java Java: Stack 36 Listen Stack Java: Stack Stack als Liste cont. Stack als Liste cont. ... public void push(Object elem) { Node v = new Node(); // neuer Knoten v.setElement(elem); v.setNext(top); // ‘Einbauen’ des neuen Knoten top = v; size++; // ‘Buchfuehrung’ fuer Groesse } ... public Object pop() throws StackEmptyException { if (isEmpty()) throw new StackEmptyException("Stack is empty."); Object temp = top.getElement(); top = top.getNext(); // Entfernen des obersten Elements size--; // ‘Buchfuehrung’ fuer Groesse return temp; } } public Object top() throws StackEmptyException { if (isEmpty()) throw new StackEmptyException("Stack is empty."); return top.getElement(); } ... D. Rösner AuD II 2009 . . . 37 D. Rösner AuD II 2009 . . . 38 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Anwendung eines Stack: Berechnung von Spannen Definition Die Spanne eines Preises an einem bestimmten Tag einer Zeitreihe sei die maximale Zahl aufeinanderfolgender Tage bis zum aktuellen Tag, an dem der Preis niedriger als oder höchstens gleich dem aktuellen Preis war. Ein quadratischer Algorithmus für Berechnung von Spannen (vgl. [GT01], Ch. 4.5]) Idee: für jedes i = 0 bis n - 1: Vergleiche P[i] mit P[i-k] für k = 0, 1, 2, ..., i solange wie P[i-k] ≤ P[i] Formal: Preisaufzeichnungen beginnen mit Tag 0 pi sei Preis am Tag i Spanne si am Tag i ist das grösste ganzzahlige k mit k ≤ i + 1 und pj ≤ pi für alle j = i - k + 1, i - k + 2, ... , i D. Rösner AuD II 2009 . . . Datenstrukturen in Java D. Rösner AuD II 2009 . . . 39 Listen Stack Datenstrukturen in Java Java: Stack 40 Listen Stack Java: Stack Ein quadratischer Algorithmus für Berechnung von Spannen Analyse von ComputeSpans1 Initialisieren und Rückgabe des Array S erfordert für jedes Element konstanten Aufwand, also O(n) repeat-Schleife innerhalb for-Schleife for-Schleife wird n-mal ausgeführt die Befehle dort ausserhalb des repeat werden n-mal ausgeführt und tragen O(n) zum Gesamtaufwand bei bei i-ter Iteration der for-Schleife wird Körper des repeat höchstens (i + 1)-mal ausgeführt [worst case: P[i] grösser als alle vorausgegangen P[i-k]] m.a.W. höchstens 1 + 2 + 3 + ... + n Beiträge aus repeat-loop, d.h. O(n (n+1) /2) = O(n2 ) Pseudocode: Algorithm computeSpans1(P) Input: ein n-el. Array P von Zahlen Output: ein n-el. Array S mit Spannen for i ← 0 to n - 1 do k←0 done ← false repeat if P[i-k] ≤ P[i] then k←k+1 else done ← true until (k > i) or done S[i] ← k return array S D. Rösner AuD II 2009 . . . 41 D. Rösner AuD II 2009 . . . 42 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Algorithmus mit linearem Aufwand Algorithmus mit linearem Aufwand Für effizientere Berechnung der Spannen: beim Übergang von Tag i - 1 auf Tag i wird der Preis am Tag i mit dem obersten Stackelement verglichen: wenn für Tag i derjenige Tag h(i) bekannt, der i vorausgeht und der - von i ausgehend - der nächste ist, an dem der Preis grösser ist als am Tag i, dann si = i - h(i) beim Berechnen der Spannen werden in einem Stack nur diese Tage mit grössten Preisen mitgeführt, d.h. i, h(i), h(h(i)), . . . D. Rösner AuD II 2009 . . . Datenstrukturen in Java ist der Preis des obersten Stackelements kleiner oder gleich dem pi wird solange pop() ausgeführt, bis entweder ein oberstes Stackelement auftaucht, dessen Preis grösser als pi , oder bis Stack leer i wird in Stack gepusht D. Rösner AuD II 2009 . . . 43 Listen Stack Datenstrukturen in Java Java: Stack 44 Listen Stack Java: Stack Ein linearer Algorithmus für Berechnung von Spannen Pseudocode: Algorithm computeSpans2(P) Input: ein n-el. Array P von Zahlen und ein leerer Stack D Output: ein n-el. Array S mit Spannen for i ← 0 to n - 1 do done ← false while not (D.isEmpty() or done) do if P[i] ≥ P[D.top()] then D.pop() else done ← true if D.isEmpty() then h ← -1 else h ← D.top() S[i] ← i - h D.push(i) return array S D. Rösner AuD II 2009 . . . Java Implementation public void computeDailyHighSpan (Quote Q[]) { int prevHigh; // closest preceding day // when the stock price was higher Stack D = new ArrayStack(); ... 45 D. Rösner AuD II 2009 . . . 46 Datenstrukturen in Java Listen Stack Datenstrukturen in Java Java: Stack Listen Stack Java: Stack Java Implementation Berechnung von Spannen Hilfsklasse: ... for (int i = 0 ; i < Q.length ; i++) { // process the current day i while ( !D.isEmpty() && Q[i].getPrice() >= ((Quote) D.top()).getPrice() ) D.pop(); if (D.isEmpty()) prevHigh = -1; // day i is a new high for the stock price else prevHigh = ((Quote) D.top()).getDay(); Q[i].setSpan(i - prevHigh); // compute and store the span D.push(Q[i]); // add to the stack the current quote } public class Quote { private int day, price, span; public Quote(int d, int p) { setDay(d); setPrice(p); } public void setDay(int d) { day = d; } public int getDay() { return day; } public void setPrice(int p) { price = p; } public int getPrice() { return price; } public void setSpan(int s) { span = s; } public int getSpan() { return span; } } } D. Rösner AuD II 2009 . . . Datenstrukturen in Java 47 Listen Stack Literatur: I Michael T. Goodrich and Roberto Tamassia. Data Structures and Algorithms in Java. John Wiley & Sons, New York, 2001. ISBN 0-471-38367-8; 2nd edition. Gunter Saake and Kai-Uwe Sattler. Algorithmen und Datenstrukturen – Eine Einführung mit Java. dpunkt.verlag, Heidelberg, 2002. ISBN 3-89864-122-8. D. Rösner AuD II 2009 . . . 49 D. Rösner AuD II 2009 . . . 48