Beruflich Dokumente
Kultur Dokumente
Übersicht
2.1 elementare Datentypen (Wiederholung Programmierung I)
2.2 Lineare Datenstrukturen (Wiederholung Programmierung I)
Klassifikationsmerkmale
Arrays
Listen
Java API (applications programmer interface)
2.3 Abstrakte Datentypen
allgemein
Stacks und Queues
2.4 Sortieren
elementare Verfahren in O(n2)
fortgeschrittene Verfahren in O(n logn)
BucketSort in O(N), N = max-min bei n Elementen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
elementare Datentypen
• Zahltypen, numerische Werte
– ganzzahlig: int, sowie byte, short, long
– floating point: double und float
• boolesche Werte
boolean mit den Werten {false, true}
• Zeichen, char
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
int
ganzzahlig: int (Default) und außerdem byte, short, long
(außerdem: BigInteger als Referenztyp)
– Wertebereich:
eine Teilmenge der ganzen Zahlen Z von –2b-1 bis 2b-1-1
interne Darstellung: Zweierkomplement mit b Bits, b=8,
16,32,64
– Operationen: alle Standardoperationen
int x,y,z; x = 1; y = x+x; x= x+y; if (x<y) x=y; z = z++;
arithmetisch:
unär: +, - (Vorzeichen), ++, -- (In-, Dekrement)
binär: +,–,*, / (ganzzahlig), % (modulo)
Vergleiche: ==, !=, <, <=, >, >=
Bitoperationen: Bitshifts <<, >>,... und Bitkomplement ~
Transfer- und Castfunktionen:
z.B. Math.ceil, Math.floor (double -> int)
Math.round (double -> long)
int vier = (int) Math.floor(Math.PI));
int drei = (int) Math.PI;
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
float, double
reell wertig: float und double (Default) z.B. double x = -0.123;
– Wertebereich:
eine Teilmenge der reellen Zahlen R
genauer: Dezimalzahlen mit beschränkter Stelligkeit (Taschenrechner)
Zahlen haben eine beschränkte Genauigkeit
intern: Binärdarstellung für Mantisse und Exponent
daher: Probleme mit numerischer Exaktheit
z.B. 0.1+ ...+0.1 (100 mal) ≠ 10.0
z.B. (1.0 / 7.0) * 7.0 ≠ 1.0
float: 32 Bit, 7 Stellen Genauigkeit, ± 1.4 E-45 ... ±3.4 E+38
double: 64 Bit, 15 Stellen Genauigkeit, ± 4.9 E-324 ... ±1.7 E+308
(„fast 0, ... sehr groß/klein“)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Referenzen
eine Referenz (pointer, Zeiger)
verweist auf ein Element eines bestimmten Typs.
Sie liefert die Speicheraddresse, wo das Element gespeichert ist.
null ist die Referenz auf das leere Element, der Zeiger ins Nichts
Derefenzierung: mit „Punkt“ greift man auf den Wert hinter der Referenz zu.
class Point {
double x; double y; x = 0.0
Point(double x, double y) { y = 0.0
this x = x; this.y = y;
} p
x = 1.0
} y = 2.0
.... q
Point p = new Point(0.0, 0.0);
Point q = new Point(1.0, 2.0);
p = q;
... dann hat p.x den Wert 1.0 und p.y den Wert 2.0
if (p.x != p.y) ...else ...;
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
class
Klassen (class) ist das zentrale Sprachelement in Java
class beschreibt Objekte (=konkrete Elemente einer Menge) und Methoden,
Methoden, die auf die Objekte einwirken (invoke a method)
und dadurch Veränderungen hervorrufen
z.B. kreieren / konstruieren / Instanziieren von neuen Objekten
mit den jeweiligen Konstruktoren unter Verwendung von new.
z.B. ändern von Attributwerten.
eBNF für class (bei eBNF steht [ ] für 0,1-mal und { } für beliebig oft)
<class_Definition> ::=
[ <class_modifier> ] class <class_name>
[ extends <superclass_name> ]
[ implements <interface> {, <interface>} ]
<class_body>
<class_body> ::=
{ { <Deklaration der KlassenAttribute> }
{ < KlassenKonstruktoren> }
{ < KlassenMethoden > }
}
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
struct
Kombination von verschiedenartigen Typen, z.B. int, double, String,…
(im Gegensatz zu Arrays oder Listen)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Stil und Namen
• guter Programmierstil
sprechende Namen (mnemotechnische Namen),
nicht a,b,c z.B. class A = ...
Großschreibung für class
Kleinschreibung für Methoden
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Arrays
Definition
Ein Array a
ist ein Feld von Elementen eines Typs ...
mit fortlaufenden Indizes von 0 bis a.length - 1
umgekehrt
zu jedem Typ T (oder AnyType, insbesondere zu jeder selbst definierten Klasse)
gibt es entsprechende Array-Datentypen (der Dimension 1,2,...)
T [ ] array a = new T [LENGTH]; // ein Array der Größe LENGTH für T-
Elemente
Beispiel:
static int MAXSIZE = ...; // eine Konstante für die Größe, zB 10
int [ ] a = new int [MAXSIZE]; // eine Arraydefinition
for (int i = 0; i < a.length; i++) a[i] = i+1; // Durchlaufen des Arrays mit for-Schleife
for (int i = 0; i < a.length; i++)
a[i] = a[a.length–1–i]; // VORSICHT! Palindrom!
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Arrays
in Java
Arrays sind vom Referenztyp.
Konsequenzen
call by reference beim Methodenaufruf;
der Verweis auf das Array wird übergeben.
void sort(int [ ] a) { ....} ;
die Zuweisung a = b
kopiert „nur“ die Referenz, nicht die Werte
int [ ] original = {3,10,-1,0, 5};
int [ ] kopie = original;
original[0] = 1000;
System.out.println(kopie[0]); // druckt 1000,
denn kopie zeigt auf original
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Array Operationen
Direktzugriff auf die Array Elemente
über den Index mit 0 ≤ index < arrayName.length
oft in Kombination mit einer for-Schleife
Anwendungen
• für lineare Abbildungen vom Rm in den Rn.
• in der Robotik zur Beschreibung von Bewegungen
• als Adjazenzmatrix für Graphen (siehe §4)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Syntax
Deklarationen von Arrays haben die Form
<Typ> [ ] <Arrayname> ;
Dadurch werden der Name und der Typ der Elemente festgelegt.
Nicht festgelegt ist so die Größe (oder die Werte der Elemente).
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
java.util.Arrays
die vordefinierte Java Klasse für Arrays in java.util
import java.util.Arrays;
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Listen
Definition
Eine Liste besteht aus einer sequentiellen Folge von Elementen desselben Typs
abstrakt: (x1,x2,...,xn)
Jedes Element xi (außer am Rand)
hat einen direkten Nachfolger next(xi) = xi+1
(und ggf. einen direkten Vorgänger prev(xi) = xi-1 )
Wesentlich:
der sequentielle Zugriff auf Elemente, auf das nächste (oder vorhergehende)
in Java
Konstruktion von Listen durch eine Verkettung von Knoten (Basisversion)
public class Node {
public int data; // der Wert der Datenelemente
public Node next; // Verzeigerung der Knoten vorwärts
(public Node prev; // Verzeigerung der Knoten auch rückwärts)
}
public class IntList {
public Node front; // Verweis auf den Listenanfang
(public Node rear; ) // Verweis auf das Listenende; falls nützlich
}
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
wichtige Methoden
Einfügen
für das Datenelement (hier der Einfachheit halber vom Typ int)
erzeugt man mit einem Konstruktur new Node (d)
einen Knoten mit dem Datenelement
und hängt den Knoten an der gewünschten Stelle in die Liste ein.
Dazu gibt es viele Vairanten (am Anfang, am Ende, ...)
Löschen
der Knoten mit dem Datenelement wird entfernt
aber die Liste darf dadurch nicht kaputt sein (zB Zerstören der Verkettung)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Basis Operationen
Was kann man mit einer Liste/Folge (x1,...,xn)
und einem neuen Element x sinnvoll machen?
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Listenoperationen
weitere Operationen:
systematischer Durchlauf durch die Liste mit einem Iterator
Teilfolge von j bis k
Konkatenation (Aneinanderhängen) von zwei Listen
Transferfunktionen in Arrays
Überscheiben/Ändern des Elements an der Stelle i
Transfer in Strings, toString()
Abfragen
auf leer, isEmpty()
der Größe, size()
Test auf Gleichheit zwischen zwei Listen x.equals(Object o)
Sortieren (wenn das bei den Elementen möglich ist)
remove_duplicates() aus der Folge (...) wird eine Menge {...} (Set in Java)
.....
im Detail:
das Java Collection Framework
insbesondere die API für LinkedList, ArrayList
Ähnliche Operationen verwendet man für Strings
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Bewertung
Wie teuer ist eine Operation auf einer (sehr langen) Liste der Länge n?
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Arten von Listen
Verkettung
einfach verkettet, mit next
doppelt verkettet, mit next und prev
zyklisch verkettet (meist mit doppelter Verkettung und cursor)
Verankerung
front (head) der Zeiger auf den Anfang der Liste, auf das erste Element
rear (tail) der Zeiger auf das Ende der Liste, auf das letzte Element
cursor ein Zeiger auf das aktuelle, zuletzt bearbeitete Element
ListenAttribut(e)
int size die aktuelle Größe der Liste (Anzahl der Elemente)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
einfache Verkettungen
einfach verkettete Listen mit (aktuell oder maximal) n Elementen
sind vollständig (oder universell)
d.h. alles was man mit Listen machen kann,
kann man (schon) mit einfach verketteten Listen machen
d.h. man kann jede sonstige Operation auf Listen simulieren,
man muss dazu die andere Operation implementieren.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
doppelte Verkettungen
doppelt verkettete Listen mit (aktuell oder maximal) n Elementen
sind vollständig (oder universell)
weil man einfach verkettete Listen mit O(1) je Operation simulieren
kann
aber: das ”O“ hat nicht den Wert ”1“
man muss den zweiten Zeiger mitschleifen und updaten.
Die effektiven Kosten sind u.U. doppelt so hoch (z.B. Stack,
Queue)
§2/ *
zyklisch verkettete Listen
© 2014 Prof. Dr. Franz J. Brandenburg
aber:
Gefahr von ∞-Schleifen beim Durchlaufen
while (n.next != null) { .....} für den aktuellen Knoten n
oft
ein blinder Knoten als Trennung und Anker für den Cursor.
Beispiel
das Josephus Problem (auch hot potato oder Reise nach Jerusalem)
zyklisch scheidet nach jeder Runde der k-nächste aus
(k fest oder mit int k = (int) random()*scale)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Gegenüberstellung
Arrays Bewertung
random access Zugriff
Direktzugriff in O(1) je Element über den Index +
in der Form a[i] oder a.get(i)
statische Größe, nachfolgend n –
festgelegt bei der Instanziierung mit new
Einfügen und Löschen –
erfordert (außer am rechten Ende) extra Shifts in O(n)
Listen
dynamisch veränderbare Größe (add, remove) +
Einfügen und Löschen (bei gegebener Stelle) in O(1) +
z.B. an den beiden Enden oder nach dem Cursor
rein sequentieller Zugriff über die Verkettung –
die Suche eines Elements dauert lange,
weil man durch die Liste iterieren muss
es gibt viele Varianten (Verkettung und Verankerung s.u.) –
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Bewertung
Wer ist der Sieger? keiner
Man braucht Arrays und Listen
Entscheidungskriterien
Welche Operationen werden vorwiegend gebraucht?
Daraus ergeben sich die Gesamtkosten (Laufzeit)
Die wichtigen/häufigsten Operationen in O(1)!
der Kompromiss (die Kombination der guten Eigenschaften mit einer optimierten Implementation)
Java API
import java.util.Iterator
import java.util.ArrayList; ein dynamisch veränderbares Array
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Generische Typen
Motivation
bei Listen und Mengen ist der Typ der verwalteten Datenelemente oft egal
Die Operationen sind immer dieselben, insbesondere stack, queue.
es gibt IntListen, DoubleListen, StringListen, uvam.
Definition
eine generische Klasse (generics) ist eine mit dem Typ T parametrisierte Klasse.
Schreibweise mit spitzen Klammern: class Name<T>
Behandlung
wie andere Typen (class) auch
Analogie
ein Stack: man stapelt Kisten vom generischen Typ T
Was in den Kisten ist, ist für das Stapeln egal.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Beispiel
T getInfo(){
return info; } // Zugriffmethoden
Node<T> getNext () {
return next;}
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Generische Typen
Anwendung von class AllTypes<T>
der Typvariablen T wird ein konkreter Typ übergeben.
Aber: keine einfachen Typen wie int, long, float, double, char, boolean
dafür: Wrapper oder Hüllenklassen
Integer, Long, Float, Double, Character, Boolean
Beispiel
Node<Integer> n1 = new Node<Integer>(new Integer(1), null);
Node<String> s1 = new Node<String>(”abrakadabra“, null);
Beachte:
kein Mix von verschiedenen Typen;
Node<Integer> und Node<String> sind total verschieden und
inkompatibel
z.B. Node<String> s2 = new Node<Integer> (17, null); // Fehler
Mehr dazu
Programmierung 2
fortgeschrittene Vorlesungen/Bücher über Java
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Iteratoren
Anwendung
das systematische Durchlaufen (einer Collection oder einer Liste)
Kurzform: for_each(x in X)
Definition
Iteratoren sind Verallgemeinerungen von Indizes (1. 2. 3....)
Der interne Aufbau bleibt verborgen (Geheimnisprinzip).
Der Iterator durchläuft die Collection/Liste von vorne bis hinten
wie eine for-Schleife (for (int i=0; i<a.length;i++))
Iteratoren sind generisch; der jeweilige Typ wird mit <T> nachgestellt.
Der Iterator steht jeweils zwischen seinem letzten und dem nächsten Element.
••
•
Start Ende
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Iteratoren
Iteratoren implementieren das java Interface Iterator.
Sei list ein geeignetes Objekt, allgemein Collection
spezieller z.B. LinkedList<T>, ArrayList<T> vom generischen Typ T
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Abstrakte Datentypen
Definition
ein abstrakter Datentyp, ADT
oder Rechenstruktur oder class oder partielle heterogene Algebra
besteht aus
(Träger- oder Daten) Mengen Ms, Mengen sind getypt
Operationen über diesen Mengen fr, partielle Funktionen
Axiomen Ax, die Eigenschaften der Operationen beschreiben.
Programme
zur Bearbeitung des ADT
verwenden Konstante und Variable für Werte der Mengen
und die üblichen Sprachelemente wie
Zuweisungen (x=x+y), if, Schleifen (for, while), Rekursion
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Beispiele für ADTs
• die boolesche Algebra
Trägermenge: {false, true}
Operationen not, and, or, xor und andere (!, &,|, ^)
Axiome: die Gesetze der booleschen Algebra
z.B. !x ≠ x, x&y = y&x, deMorgan, distributiv,...
int in Java
Trägermenge: {–231,...+231-1}∪{underflow, overflow} und boolean
Operationen die Standardoperationen mit Über- bzw. Unterlauf
Axiome: die Rechenregeln auf beschränkten Zahlbereichen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Beispiele für ADTs
• Strings
Trägermenge: ∑* und int und boolean
ein Referenztyp in Java
Operationen: die klassischen String Operationen nach Java API
z.B. String emptystring = new String( ); erzeugt das leere Wort
int laenge = s.length(); liefert die Anzahl der Zeichen
außerdem
String concat ( String s) die Konkatentation
s+t die Konkatentation
String substring ( int a, int b) das Teilwort von a bis b-1 der Länge b-a
boolean equals ( Object o) true, falls o ein String und identisch
int compareTo ( String s) 0 falls Gleichheit,
negativ, falls s lexikographisch größer
positiv, falls s lexikographisch kleiner
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Implementierung
Definition
eine Implementation J eines ADT D über einem ADT D‘
beschreibt, wie man Probleme über D in D‘ löst.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Dominanz
Definition
ein ADT D‘ dominiert einen ADT D, kurz D ≤ D‘
wenn jede Operation von D in O(1) in D‘ implementiert wird
werden kann.
Beispiele
– D einfach verkettete Liste, D‘ doppelt verkettet,
aber bei jedem Einfügen oder Löschen muss man auch den
Vorgängerzeiger pred updaten
Wir müssen den zweiten Zeiger „mitschleifen“.
– D Liste ohne size Attribut, D‘ mit size Attribut.
Das size Attribut ist nützlich z.B. für size() und isEmpty(),
aber size muss bei jedem add und remove geändert werden.
Wir müssen size „mitschleifen“.
– D = Stack und D‘ array (oder einfach verkettete Liste)
- D = Queue und D‘ array oder einfach verkettete Liste
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Nicht-Dominanz
... ist der Normalfall
Jeder Datentyp, jede Datenstruktur hat Stärken und Schwächen.
Stärke: eine gut unterstützte Operation, oft O(1)
Schwäche: eine nicht unterstützte Operation, meist O(n)
Beispiele
Arrays sind schlecht für Einfügen und Löschen (außer am rechten Ende)
Einfügen (oder Löschen) auch ganz vorne kostet O(n),
sei size die Anzahl der „echten“ Elemente in a,
denn es müssen alle anderen Elemente im Array geshiftet werden.
for (int i = size-1; i >= 0; i--) a[i+1] = a[i]; size ++; //Rechtsshift um ein Feld
for (int i = 1; i< size; i++) a[i-1] = a[i]; size--; // Linksshift um ein Feld
Arrays sind gut für den Zugriffe (Suchen, Ändern des Wertes) auf das i-te Element
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Nicht-Dominanz
Beispiele
einfach verkettete Listen sind schlecht für das
Einfügen und Löschen vor dem aktuellen Element
Das aktuelle Element ist das Listenelement xi unterm Cursor
denn man muss wieder von vorne bis zu i-1 laufen.
Das kostet O(i-1) und das ist worst case O(n)
Listen (allgemein) sind schlecht für den wahlfreien (random access) Zugriff
auf das i-te Element.
Es dauert (im Mittel) O(n),
bis man mit einem Cursor an der richtigen Stelle ist, denn
bei n Elementen laufe bis zur Stelle i, 1≤i≤n
im Mittel (Durchschnitt)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Spezialfälle
Es gibt Spezialfälle von listenähnlichen ADTs,
bei denen das dynamische Verändern durch Einfügen und Löschen
nur an den Rändern vorgenommen wird.
nämlich
Stack, Keller LIFO last in first out (als Regenbogen)
Queue, Puffer FIFO first in first out (mit Zylinder-Darstellung)
Deque „alles hat ein Ende nur die Wurst hat zwei“
Doppelstack zwei Stacks (im Array mit „Luft“ in der Mitte)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Stack
Definition
Ein Stack (pushdown stack) oder Keller (oder Stapel)
ist ein abstrakter Datentyp für beliebige Elemente
Elemente sind vom Typ Object, oder speziell oder generisch <T>
mit den Operation: push(T e); // Einfügen des Elements e vom Typ T
und pop(); // Löschen des obersten Elements
und zusätzlich Stack<T>(), isEmpty(), top()
Ein Stack operiert nach dem LIFO Prinzip, last in, first out.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Stacks
Entdecker
für die Informatik (Anwendungen z.B. im Compilerbau)
hat Prof. F.L. Bauer (Emeritus, TU München) um 1962
den Stack und seine Bedeutung und besonderer Rolle entdeckt.
Beispiele:
Folge der Aktionen beim Stapeln von Tellern
korrekte Klammerungen ((([[ () ] ([]) ] )) ())
Traversieren (Durchlaufen) von Bäumen
Auswertung von Ausdrücken, postfix Notation
Rekursion (der Aufrufkeller)
Rangieren von Waggons („Keller-Permutationen“)
nicht: in der Mensa: „Wer zuletzt kommt, wird als Erster bedient.“
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Visualisierung
Ein Stack ist ein Regenbogen.
Für jede Aktion (Einfügen von x) und Löschen von y zum Zeitpunkt t
gibt es einen Knoten oder Punkt auf der X-Achse;
vereinfacht: die Zeitpunkte 1,2,…,2n
für das Einfügen/Löschen von n Elementen
Verbinde die Zeitpunkte von Einfügen und Löschen durch einen Bogen,
der oberhalb der X-Achse verläuft.
Die Aktionen sind LEGAL für einen Stack ⬄ es gibt einen Regenbogen.
§2/ *
Implementierung von Stacks
© 2014 Prof. Dr. Franz J. Brandenburg
– im Array
mit einem Kellerpegel p
gefüllt von a[0] bis a[p-1]
p zeigt auf das erste freie Arrayfeld
isempty == (p=0)
einfügen bei a[p]; p++;
löschen durch p--
ohne Überschreiben von a[p] Gefahr!!
mit vorherigem Überschreiben von a[p]
Shifts
keine
Überlauf
nur wenn das Array total voll ist
Kosten je Operation
je O(1) (genauer: 2 Aktionen)
§2/ *
Implementierung von Stacks
© 2014 Prof. Dr. Franz J. Brandenburg
– als Liste
es reicht eine einfach verkettete Liste (und diese generisch <T>)
denn Einfügen und Löschen erfolgt am Kopf der Liste
mit O(1) (genauer: 2) je Operation
Kosten je Operation:
je O(1), genauer 2 Aktionen bei „guter“ Implementierung
Hinweis:
doppelt verkettete Liste ist „Overhead und Verschwendung von Ressourcen“
weitere Ergänzungen
size ist ok
aber
boolean contains(T x) Ist x irgendwo im Keller
ist illegal (für Stack)
denn das Kellerprinzip, LIFO und zerstörendes Lesen ist verletzt.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Java Stack
das Interface java.util.Stack<T>
erbt alle Methoden von der Java Klasse Vector.
(Vector ist sehr ähnlich zu ArrayList)
aber Vector enthält viele Methoden, die das Kellerprinzip verletzen.
Methoden
Stack<T>() Konstruktor für den leeren <T>-Keller
boolean empty()
T push (T item) lege item oben auf den Keller
T pop() das oberste Element; dieses wir gelöscht
EmptyStackException bei einem Fehler
T peek() Zugriff auf das oberste Element, ohne Löschen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Beispiel Stack
import java.util.Stack;
....
Stack<String> stringStack = new Stack<String>; // ein generischer Typ, hier Strings
stringStack.push(”erstens”);
stringStack.push(”zweitens”);
stringStack.push(”drittens”);
boolean b = stringStack.isEmpty(); // liefert false
String s;
s = stringStack.pop(); // liefert drittens
s = stringStack.pop(); // liefert zweitens
s = stringStack.peek(); // liefert erstens
s = stringStack.peek(); // liefert erstens
s = stringStack.pop(); // liefert erstens
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Queue
Definition
Eine Queue (Puffer oder Schlange (Achtung ≠ priority queue))
ist ein abstrakter Datentyp für beliebige Objekte vom Typ T
mit den Operation: offer(T e); ein Objekt hinten anfügen, append
poll(); das erste Element löschen, pop
(und zusätzlich create(), isempty(); T peek() Zugriff auf das erste Element)
Eine Queue arbeitet nach dem FIFO Prinzip, first in, first out.
Axiome
Einfügen und dann Löschen erzeugt i.a. keinen Gleichzustand.
Löschen eines Elements x und dann Wieder-Einfügen von x
ergibt fast immer eine andere Queue (geänderte Reihenfolge)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Puffer
Entdecker:
unbekannt
Beispiele:
Engländer an der Bushaltestelle
first come, first served
Stau auf einer einspurigen Strasse:
auf den Stau auflaufen (einfügen) und rausfahren (löschen)
überholen ist bei einer Schlange (Puffer) verboten
Puffern von Jobs (Scheduling)
Breitensuche in Graphen (später)
Rangieren von Waggons („Puffer-Permutationen“)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Visualisierung
Queue = Maibaum
man wickelt die Aktionen um einen Zylinder.
Für jede Aktion (Einfügen von x) oder Löschen von y zum Zeitpunkt t
gibt es einen Knoten oder Punkt auf der X-Achse
Diese liegt vorne auf einem rollenden Zylinder.
Alternativ: man schneidet diese Achse auf und erhält
ein oberes und ein unteres Level.
Verbinde die Zeitpunkte von Einfügen und Löschen durch eine Kurve,
die um den Zylinder (Maibaum) läuft
oder nach dem Aufschneiden gerade zwischen den beiden Leveln.
Es gilt: Queue ⬄ legale kreuzungsfreie Zeichnung
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Queue im Array
im Array
betrachte ein Array als Ringpuffer der an den Enden zusammengeklebt ist.
Dies erreicht man durch Rechnen modulo n mit n=a.length.
Der Inhalt der Schlange (des Puffers) steht zwischen zwei Cursorn
front zeigt auf das „letzte“ (zuletzt eingefügte) Element, dort lesen.
rear zeigt auf das „erste“ freie Feld im Array, dort schreiben.
Shifts: keine
Kosten je Operation
je O(1) (genauer: 2 Aktionen)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Queue als Liste
• es reicht eine einfach verkettete Liste
mit 2 Cursorn für front und rear
Die Schlange wandert von links nach rechts
rechts einfügen, bei front
links löschen, bei rear
Anmerkung:
hier sind alle Datenobjekte public und damit von außen zugreifbar.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Java Queue
das Interface java.util.Queue<T>
erbt alle Methoden von de Java Interface Collection
und erweitert Collection<T>
Methoden
Queue() Konstruktor für den leeren Keller
boolean empty()
boolean offer (T item) Einfügen (falls ok, sonst false)
T poll() Rückgabe und Löschen des ersten Elements
T peek() Rückgabe des ersten Elements (oder null)
außerdem
die ererbten Methoden von Collection (nicht FIFO Prinzip)
wie void add(T item); T remove(); T element(); int size();
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Permutationen
betrachte:
Waggons mit den Nummern 1, 2, 3,.... und rangieren
mit einem Stack (Prellbock)
mit einem Ausweichgleis (queue).
Welche Permutationen sind möglich?
stac queue
k
(1,2,3)->(2,3,1) geht nicht LIFO (1,2,3)->(3,2,1) geht nicht FIFO
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Deque
eine Deque (double ended queue)
ist ein ADT mit Einfügen und Löschen an beiden Enden
insertLeft(T x); insertRight(T x);
T removeLeft(); T removeRight();
T getfromleft(); T getfromright();
Implementierung
doppelt verkettete Liste
Kosten:
O(1) je Operation
Stack ist Spezialfall mit insertLeft(..) für push und removeLeft() für pop();
Queue ist Spezialfall mit insertLeft(..) für offer und removeRight() für poll();
Anwendungen:
keine (außer bei Klausuren)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Visualisierung
SATZ: DEQUE ⬄ (aufgeschnittener) Zylinder
Es gibt je einen Stack auf jeder Seite und ab und zu klaut ein Stack
dem anderen Elemente. Diese Elemente verhalten sich wie eine Queue.
Hintergründe:
C. Auer et al. Plane Drawings of Queue and Deque Graphs,
Proceedings Graph Drawing 2010, Springer LNCS 6502, 68-79, 2011,
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Doppelstack
gegeben: 2 Stacks leftStack, rightStack
Implementierung
in 1 Array mit 2 Kellerpegeln
Operationen: je O(1)
Overflow: Array voll, wenn sich die Kellerpegel kreuzen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Java API
Listenartige Strukturen
sind so wichtig und treten so häufig auf
dass sie in der API vordefiniert und optimiert implementiert sind.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Klassifikation
konkrete Klassen
§2/ *
Dynamisch veränderbare Arrays
© 2014 Prof. Dr. Franz J. Brandenburg
Trick:
wenn das aktuelle Array a voll ist,
wird ein neues, doppelt so großes Array b erzeugt.
Der Inhalt von a wird nach b umkopiert und a=b gesetzt
Auch wenn man dies wiederholt machen muss, sind die Gesamtkosten
für das Neuanlegen und Umkopieren nur 4*N, N= größes Array am Ende
int size sei die Anzahl der aktuell belegten Array Elemente
int capacity sei die statische Größe (die beim Anlegen des Arrays festgelegte length)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Sortieren
Voraussetzung:
man kann Elemente paarweise vergleichen
einfach: ≤
in Java oft: Comparable
in der Praxis: ≤ auf Zahlwerten
≤ die lexikographische Ordnung auf Strings
Algorithmen
insertionSort O(n2) mit Listen, O(n logn) mit Bäumen
selectionSort O(n2)
bubbleSort O(n2)
mergeSort O(n logn)
quickSort O(n logn) im Mittel, O(n2) worst case
heapSort O(n logn)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Gütekriterien
Wie kann man Sortierverfahren vergleichen?
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
selectionSort
selectionSort Kosten
Idee:
wähle in jedem Schritt das jeweils kleinste Element O(n-i)
(von links nach rechts in der Liste, dann robust)
lösche es und füge es mit add in O(1) O(1)
in die sortierte Liste/Array ein (eine Hilfsliste oder mit swap)
Implementation:
zwei for-Schleifen daher O(n2)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
insertionSort
insertionSort Kosten
Idee:
wähle das nächste Element (Iterator, for_each) a[i]
füge es an der richtigen Stelle in der sortierten Liste ein O(i)
im Array muss man (nach rechts) shiften
in der Liste die richtige Stelle suchen
im Suchbaum geht das in O(log i)
Implementation:
zwei for-Schleifen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
bubbleSort
bubbleSort Kosten
Idee
vertausche zwei benachbarte Elemente, wenn sie „falsch“ stehen
große Elemente „blubbern“ nach oben
falls kein swap mehr nötig ist, break.
Implementation:
zwei for-Schleifen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
bubbleSort
public static void fastBubbleSort(int [ ] a) {
boolean fertig = false;
for (int i = 0; !fertig; ++i) { // vorzeitiger Abbruch
fertig = true;
for (int j=0; j < a.length-i-1; j++) // die letzten i Elemente sind schon ok
{
if (a[j] > a[j+1])
{
swap(a[j], a[j+1]); // int temp = a[j]; a[j]=a[j+1]; a[j+1]=temp;
fertig = false; // es bleibt noch was zu tun
}
}
}
}
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
bubbleSort
Beobachtungen
– pro Durchlauf (der äußeren Schleife) werden meist mehrere
Elemente vertauscht und nach oben befördert.
– ganz schlecht für bubbleSort:
eine falsch herum sortierte Liste, ≥ statt ≤
Dann bewegt jeder Durchlauf nur ein Element.
– Gegenmaßnahme: Shearsort (oder Shakersort)
abwechselnd wird nach oben (for ...; i++) und swap bei > geblubbert
und dann abwärts (for ...; i--) nach unten (swap) gebubbelt.
- fastTermination, wenn in einer Phase keine Vertauschung vorkommt.
- Invariante für die Korrektheit:
das (jeweils) größte Element wandert nach oben
und das jeweils kleinste Element wandert nach unten
nach i auf+ab Runden hat man an Grenzen for(int j=i; j < a.lenth-i, ...)
– für kleine n ist fastbubblesort „schnell“
und eine gute Alternative auch für die rekursive Endphase von quickSort
– bubbleSort (oder Shearsort) ist sehr gut,
wenn nur wenige Elemente nicht am richtigen Platz stehen.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
mergeSort(1)
mergeSort Kosten
Idee: ein divide&conquer Verfahren
public static void mergeSort(int [ ] a);
if (a.length <= 1) return a
else (a1, a2) = divide(a) für zwei gleichgroße Teile a1 und a2
b1 = mergeSort(a1); b2 = mergeSort(a2);
merge(b1, b2);
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
mergeSort(2)
Kosten
Berechnung der Kosten nach dem Mastertheorem
T(n) = 2•T(n/2) + O(n) ergibt O(n logn)
Beispiel
[-5,3,17,-1, 0,9,13,1]
[-5,17,0,13] [3,-1,9,1]
Zerlegen
[-5,0] [17,13] [3,9] [-1,1]
-5 0 17 13 3 9 -1 1
[-5,-1,0,1,3,9,13,17]
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
mergeSort(3)
Korrektheit
per Indukion und Rekursion
n=0 ok
n/2 --> n die rekursiven Aufrufe für a1 und a2 seien korrekt.
Dann konstruiert merge die korrekte Gesamtsortierung.
Beweis für die Korrektheit von merge:
durch Induktion über die Summe der Länge beider Listen
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
quickSort(1)
Idee
public static void quickSort(int [ ] a,int start, int end]
Bemerkung
experimentell ist quickSort am schnellsten
Variante mit bubbleSort oder if-else Kaskade für kleine Werte n≤5
Dies beschleunigt die Laufzeit, weil die Rekursion angekürzt wird.
Das ist wie Tuning von F1-Autos.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Vergleiche
die minimale Anzahl an Vergleichen (Quelle D.E. Knuth, The Art of Computer programming, 1968)
n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# 0 1 3 5 7 10 13 16 19 22 26 30 34 38 42 46 50
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
untere Schranke
auf großen Zahlen (bei ≤) gehts nicht besser als Ω(n logn)
Definition
ein Entscheidungsbaum (oder Vergleichsbaum) ist ein Binärbaum
für die Eingabe x1,x2,...,xn (an der Wurzel)
mit inneren Knoten für die Prädikate „x≤y“
und ausgehenden Kanten gemäß „ja“ oder „nein“
und jede mögliche Lösung (jede Permutation π) xπ(1),xπ(2),...,xπ(n)
mit xπ(i) ≤ xπ(i+1) für i=1,..,n-1
in mindestens einem der Blätter,
so dass (xπ(1),xπ(2),...,xπ(n)) die Prädikate auf den Kanten erfüllt.
Beispiel
ein Entscheidungsbaum für x,y,z
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
untere Schranke
Satz
jedes allgemeine Sortierverfahren benötigt
im Entscheidungsbaummodel, d.h. gestützt auf ≤ -Vergleiche
Ω(n logn) viele Vergleiche und (damit) Ω(n logn) Zeit.
M.a.W. n logn ist untere (und obere) Schranke für Sortieren.
Beweis
O.b.d.A. seien die Elemente paarweise verschieden, xi ≠ xj.
Jedes Blatt des Entscheidungsbaums beschreibt eine Folge
xπ(1) < xπ(2) < ... < xπ(n)
Es gibt n! Permutationen π,
jede steht mindestens einmal an einem Blatt
⇒ #Blätter ≥ n!
⇒ Höhe(Entscheidungsbaum) ≥ log(#Blätter) ≥ log(n!) ≥ Ω(n logn).
Die Höhe des Entscheidungsbaums
ist die maximale Anzahl der Vergleiche auf einem Berechnungspfad
ist somit eine untere Schranke für die Laufzeit.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
BucketSort
ein anderes Szenario:
gegeben: viele kleine Zahlen, oBdA 0 ≤ xi ≤ N, 1 ≤ i ≤ n
gesucht: eine Sortierung einer bliebigen Liste solcher Zahlen
Anwendung: Briefe sortieren
per Postfach
nachder Postleitzahl - fünfstellig
allgemein: Hashing mit chaining (§3)
Idee: die Zahlen seien k-stellig, z.B. k=5 für Postleitzahlen
k Runden (for int i =1, i<=k,i++)
1. Runde: sortiere die Zahlen nach der 1. Stelle
gleichwertige Zahlen (1. Stelle) kommen in denselben Sack
i- te Runde, i≥2
alle Zahlen in einem Sack werden nach der i-ten Stelle
sortiert
und in Säcke aufgeteilt
Komplexität: k•n (für n Zahlen)
denn k Runden; je Runde wird jede Zahl „angefasst“
gesamt: O(n) wenn k klein ist (wie bei Postleitzahlen)
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Bucketsort
eine Variante
alle zu sortierenden Elemente xi (einfacher Zahlen)
liegen wertmäßig im Intervall [min, max] mit max-min = N
Bemerkung
die untere Schranke Ω(n log n) gilt hier nicht
denn Bucketsort basiert NICHT auf Vergleichen von Zahlen
sondern auf deren absoluten Werten.
Für große Zahlbereiche oder float/double geht Bucketsort nicht.
§2/ *
© 2014 Prof. Dr. Franz J. Brandenburg
Specials
Definition
Die Inversionsdarstellung zum Sortieren von Zahlen (x1,….,xn)
ist ein Vektor der Länge n (s1,….,sn) mit
si = #Zahlen links von xi, die kleiner sind als xi
Die Summe der si ist die Anzahl der Inversionen oder bubbleSort Schritte
Die Kendall-tau Distanz zwischen zwei Permutationen a und b gibt an, wie
oft a[i] < a[j] und b[j] < b[i] gilt.
D.h. Wie oft widersprechen sich a und b.
§2/ *
Sortieren bei DNA-Sequenzen
© 2014 Prof. Dr. Franz J. Brandenburg
Frage:
Wie viele Inversionen + Transpositionen braucht man zum Sortieren?
Antwort: das ist ein hartes Problem: NP-schwer (siehe §5)
§2/ *