Foliensatz 6 - Benutzer-Homepage

Werbung
Algorithmen und
Datenstrukturen CS1017
Th. Letschert
TH Mittelhessen Gießen
University of Applied Sciences
Datenstrukturen I: Lineare Sequenzen / Listen
– Datenstrukturen für Sequenzen
– Sequenzen in der Java-API: Listen und Ströme
– Iteratoren
– Algorithmen auf Sequenzen
Lineare Sequenzen / Listen
Funktionaler Datentyp Liste
Eine Liste ist eine endliche Kollektion von Werten, bei der jedem Wert eine eindeutige
Position aus dem Bereich der natürlichen Zahlen zugeordnet ist.
(a, b, c)
~
0 → a,
1 → b,
2 → c
Listen-Werte:
Alle Sequenzen (endliche total geordnete Folgen) von Element-Werten
Operationen:
– Den Wert an (Index-) Position i feststellen
– Feststellen der Länge (Anzahl der Elemente)
Seite 2
Lineare Sequenzen / Listen
Imperativer Datentyp Liste
Eine imperative (veränderliche) Liste ist eine endliche Kollektion von Variablen, bei der
jeder Variablen eine eindeutige Position aus dem Bereich der natürlichen Zahlen
zugeordnet ist.
0 → a ,
( a ,
b ,
c )
~
1 → b ,
2 → c
Listen-Variable:
Alle veränderlichen Sequenzen (endliche total geordnete Folgen) von Variablen
Operationen:
– Den Wert an (Index-) Position i feststellen
– Feststellen der Länge (Anzahl der Elemente)
– Ein Element an einer bestimmten Position einfügen
– Eventuell weitere abgeleitete Operationen (Vorgänger / Nachfolger, etc.)
– Eventuell Beschränkungen bei den Einfüge- und Zugriffs-Operationen
(Nur am Anfang / Ende der Sequenz)
– ...
Seite 3
Lineare Sequenzen / Listen
Im Zusammenhang mit Listen werden viele Begriffe gebraucht die eine völlig unterschiedliche,
ähnliche oder gleiche Bedeutung haben.
Es ist wichtig zu wissen, was genau gemeint ist, bzw. gemeint sein könnte und selbst eine klare
Ausdrucksweise zu benutzen.
Liste als (Daten-) Typ
Alles mit einer von aussen beobachtbaren linearen Ordnung*
Folge, Sequenz, lineare Sequenz, Vektor, Strom, … sind weitere Bezeichnungen
Unterscheidung:
– funktional (= unveränderlich)
„ist unveränderlich“ ~ „ist ein Wert“
– imperativ (= veränderlich)
Die Liste und eventuell aber nicht unbedingt auch ihre Elemente können verändert
werden
„ist veränderlich“ ~ „ist eine Variable“
*siehe Veranstaltung Diskrete Mathematik oder
https://de.wikipedia.org/wiki/Ordnungsrelation
Seite 4
Lineare Sequenzen / Listen
Liste als (Daten-) Struktur
Alles was tatsächlich in einer linearen Ordnung angeordnet ist
Weitere Bezeichnungen: Folge, Sequenz, lineare Sequenz, Vektor, Strom, …
Auch hier kann zwischen veränderlich und unveränderlich unterschieden werden.
Datenstrukturen werden aber in der Regel als veränderliche Beziehungen realisiert,
auch wenn sie von aussen unveränderlich zu sein scheinen
Seite 5
Imperative Listen (lineare Sequenzen) in der Java-API
Imperative Listen / Sequenzen in der Java-API
Imperative (Veränderliche) Listen werden in unterschiedlichen Varianten von der Java-API zur
Verfügung gestellt
Interfaces (Varianten in den möglichen Operationen)
– java.util
Interface List<E>
Einfügen und Entnehmen an beliebigen Positionen möglich
– java.util
Interface Queue<E>
Einfügen nur an einem Ende, Entnehmen nur am anderen Ende möglich
– java.util
Interface Deque<E>
Einfügen und Entnehmen nur an den beiden Enden möglich
– …
Klassen (ADTs, Varianten in der Implementierung der Interfaces)
– java.util
Class ArrayList<E>
Array-basierte Listenimplementierung
– java.util
Class Vector<E>
Array-basierte Listenimplementierung mit synchronisierten Operationen
– java.util
Class ArrayDeque<E>
Array-basierte Listenimplementierung mit Einfügen und Entnehmen nur an den beiden Enden
möglich
– …
Seite 6
Funktionale Listen (lineare Sequenzen) in der Java-API
Funktionale Listen / Sequenzen in der Java-API: Streams
Die Java-API unterstützt ab Java-8 auch die funktionale Programmierung
Dazu gehören funktionale / unveränderliche Listen mit speziellen Operationen
Streams
java.util.stream.Stream ist ein Interface
– es bietet eine funktionale Sicht auf nicht-funktionale Listen / Sequenzen
– Beispiel:
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class Streams_App {
public static void main(String[] args) {
List<Integer> lst = Arrays.asList(new Integer[]{1,2,3,4,5,6,7,8,9,10});
int sum1 = 0;
for (int i: lst) {
sum1 = sum1+i;
}
System.out.println(sum1);
Klassisch imperativ
auf imperativer Liste:
Summe mit Iterator
Imperative Liste bekommt
funktionales Gesicht und
wird funktional summiert
Stream<Integer> strm = lst.stream();
int sum2 = strm.reduce(0, (a, x) -> a+x );
}
System.out.println(sum2);
}
Seite 7
Datenstrukturen für Listen
Listen auf Basis von Arrays
Listen können mit Hilfe von Arrays implementiert werden
Datentyp Liste
Vorteil
Datenstruktur Array
Effiziente Zugriffsoperationen
Nachteil
Aufwendiges Einfügen und Entfernen im Inneren
Aufwendiges Vergrößern
Eventuell Verschwendung von Speicherplatz
Array-basierte Liste
Implementierungen in der Java-API
– ArrayList<E>
– Vector<E> veraltet
– Stack<E> veraltet
Verwenden Sie die Klasse ArrayList, es sei denn
Sie wissen, dass und warum diese nicht geeignet ist.
Verwenden Sie dann eine andere Klasse aus der API.
Gibt es nichts Geeignetes dann implementieren Sie
eine Klasse auf Basis von java.util.AbstractList.
Seite 8
Datenstrukturen für Listen
Eigene Listenimplementierung auf Basis von Arrays
Listen auf Basis von Arrays sind leicht zu implementieren
public class MyArrayList<E> {
private int topIndex =
private int arraySize =
Aufgabe:
0; // index of first empty position
10; // actual array size
Ist dies eine Implementierung von
java.util.List<E> ?
@SuppressWarnings("unchecked")
private E[] array = (E[]) new Object[arraySize];
@SuppressWarnings("unchecked")
public boolean add(E element) {
if (topIndex == arraySize) {
E[] arrayTemp = (E[]) new Object[arraySize+10];
System.arraycopy(array, 0, arrayTemp, 0, arraySize);
array = arrayTemp;
arraySize = arraySize+10;
}
array[topIndex++] = element;
public static void main(String[] args) {
return true;
MyArrayList<String> l = new MyArrayList();
}
for (int i=0; i<15; i++) {
l.add("blub "+i);
}
public E get(int index) {
return array[index];
}
}
System.out.println(l.size());
public int size() {
return topIndex;
}
}
for (int i=0; i<l.size(); i++) {
System.out.println(l.get(i));
}
Seite 9
Datenstrukturen für Listen
Eigene Listenimplementierung auf Basis von Arrays
Listen auf Basis von Arrays sind leicht zu implementieren
Allerdings erfüllt eine einfache Implementierung nicht das Interface java.util.List<E>
public class MyArrayList<E> implements List<E> {
…
Es fehlen sehr (!) viele
Methoden die im Interface
deklariert sind!
}
Seite 10
Datenstrukturen für Listen
Eigene Listenimplementierung auf Basis von Arrays
Die Java-API bietet eine Basisklasse zur Erleichterung der Implementierung eigener Array-basierter
Listen: java.util Class AbstractList<E>
Implementierungsskelett für eigene Listenimplementierungen durch Ableitung
import java.util.AbstractList;
import java.util.List;
public class MyArrayList<E>
extends AbstractList<E> implements List<E> {
private int topIndex =
private int arraySize =
0; // index of first empty position
10; // actual array size
@SuppressWarnings("unchecked")
private E[] array = (E[]) new Object[arraySize];
}
@SuppressWarnings("unchecked")
@Override
public boolean add(E element) {
if (topIndex == arraySize) {
E[] arrayTemp = (E[]) new Object[arraySize+10];
System.arraycopy(array, 0, arrayTemp, 0, arraySize);
array = arrayTemp;
arraySize = arraySize+10;
}
array[topIndex++] = element;
return true;
}
@Override
public E get(int index) {
return array[index];
}
@Override
public int size() {
return topIndex;
}
Seite 11
Die fehlenden Methoden
werden von AbstractList
geliefert!
Datenstrukturen für Listen
Verwendungs-Beispiel der selbst definierten Liste:
public static void main(String[] args) {
Fragen:
List<String> l = new MyArrayList<>();
1. Warum gibt add true zurück?
for (int i=0; i<15; i++) {
l.add("blub "+i);
}
2. Funktionieren beide (überladenen)
Varianten der Methode add?
3. Funktionieren die Schleifen?
l.add(5, "Hi");
4. Wie nennt man die Form der dritten Schleife?
System.out.println(l.size());
5. Implementiert die Klasse das
Interface Iterable<E>?
for (int i=0; i<l.size(); i++) {
System.out.println(l.get(i));
}
}
for (String s : l) {
System.out.println(s);
}
Seite 12
Datenstrukturen für Listen
Verwendungs-Beispiel der selbst definierten Liste:
public static void main(String[] args) {
Fragen:
List<String> l = new MyArrayList<>();
1. Warum gibt add true zurück?
Das Interface List verlangt es so
for (int i=0; i<15; i++) {
l.add("blub "+i);
}
2. Funktionieren beide (überladenen)
Varianten der Methode add?
Nein, nur die erste, siehe API zu AbstractList
l.add(5, "Hi");
3. Funktioniert die Schleifen?
Ja!
System.out.println(l.size());
4. Funktioniert die zweite Schleife?
Ja!
for (int i=0; i<l.size(); i++) {
System.out.println(l.get(i));
}
}
5. Wie nennt man die Form der dritten Schleife?
for each Schleife
for (String s : l) {
System.out.println(s);
}
6. Implementiert die Klasse das Interface Iterable<E>?
Ja:
MyArrayList
~extends~>
AbstractList
~implements~> List
~extends~>
Iterable
Seite 13
Iteratoren
for-Each-Schleife ~> Iterator
Elegantes Durchlaufen einer iterierbaren Kollektion
Basiert auf einem Iterator.
Iterator:
Mechanismus zum Durchlaufen einer Kollektion
ohne deren innere (Daten-) Strukturen kennen zu
müssen.
Iterator<Integer> iter = c.iterator();
Eine ordentliche Kollektion ist iterierbar !!
Sie implementiert das Interface Iterable
Sie hat eine Methode Iterator
diese Methode liefert einen Iterator.
while ( iter.hasNext() ) {
System.out.println( iter.next() );
}
automatische
Umwandlung durch den
Compiler
for (int i : c )
System.out.println(i);
Die Maus (der Iterator)
agiert im Hintergrund
Seite 14
Iteratoren
Kollektion
Iterable und Iterator
Iterable – Die Kollektion
Iterator – Der Mechanismus um die Werte der Kollektion zu durchlaufen
Ein Iterator kann das jeweils nächste Element zur Verfügung stellen
und testen, ob alle Elemente durchlaufen wurden.
Iterator
Sinn – Die Kollektion kann durchlaufen werden ohne den Aufbau und die
Organisation der Kollektion kennen zu müssen
Geheimnisprinzip: Ich will nicht jeden in meinen Keller lassen der bei
mir eine Flasche nach der anderen trinkt!
java.lang Iterable<T>
java.lang Iterable<T>
Iterator<T> iterator();
Iterator<T> iterator();
iterator
java.util List<T>
java.util List<T>
java.lang Iterator<T>
java.lang Iterator<T>
next():T
next():T
hasNext():boolean
hasNext():boolean
java.util AbstractList<T>
java.util AbstractList<T>
MyArrayList<T>
MyArrayList<T>
Seite 15
iterator
Iteratoren
Iterator
Mechanismus zum Durchlaufen einer Kollektion
– deren inneren Aufbau man nicht kennt
– und der nicht offen gelegt werden soll.
Die Kollektion stellt ihren Nutzern einen Iterator zur Verfügung
Sie ist damit iterierbar (erfüllt java.lang Interface Iterable<T>)
Ein Iterator kann das jeweils nächste Element zur Verfügung stellen und testen, ob alle
Elemente durchlaufen wurden.
Jede Kollektion, die Sie schreiben, muss iterierbar sein – es sei denn Sie haben wirklich gute
Gründe, die dagegen sprechen.
Seite 16
Iteratoren
Beispiel: Array-basierte Liste mit eigenem Iterator
import java.util.Iterator;
public class SimpleIterableList<E> implements Iterable<E> {
private int topIndex =
private int arraySize;
private E[] element;
0; // index of first empty position
// maximal size
public SimpleIterableList(int maxSize) {
this.arraySize = maxSize;
element = (E[]) new Object[this.arraySize];
}
public void add(E e) {
if (topIndex == arraySize)
throw new IllegalStateException();
element[topIndex++] = e;
}
@Override
@Override
public
Iterator<E> iterator() {
public Iterator<E> iterator() {
return new Iterator<E>() {
return new Iterator<E>() {
private int pos = 0;
private int pos = 0;
@Override
@Override
public
boolean hasNext() {
public
boolean
{
return
pos hasNext()
< topIndex;
return
pos
<
topIndex;
}
}
public E get(int index) {
if (index < 0 || index >= topIndex)
throw new IllegalArgumentException();
return element[index];
}
@Override
@Override
public
E next() {
public
E next()
{
return
element[pos++];
return
element[pos++];
}
}
}
@Override
@Override
public
void remove() {
public
void remove() {
throw
throw
new UnsupportedOperationException();
new UnsupportedOperationException();
}
}
};
};
Seite
} 17
}
Datenstrukturen für Listen
Listen auf Basis von verketteten Zellen (verkettete Listen)
Listen können mit Hilfe von verketteten Zellen implementiert werden
Vorteil
Datentyp Liste
Schnelles Einfügen / Entfernen
Datenstruktur
Nachteil
Langsamer Indexzugriff
Implementierungen in der Java-API
Liste mit Verkettung
– LinkedList<E>
Verwenden Sie die Klasse LinkedList, wenn Einfügeund Entnahme-Operationen häufig vorkommen.
Seite 18
Datenstrukturen für Listen
Listen auf Basis von verketteten Zellen (verkettete Listen)
Die Java-API bietet eine Basisklasse zur Erleichterung der Implementierung eigener
verketteter Listen
java.util Class AbstractSequentialList<E>
Implementierungsskelett für eigene Listenimplementierungen durch Ableitung
Komplexe Aufgabe da ein ListIterator implementiert werden muss.
Seite 19
Datenstrukturen für Listen
Beispiel verkettete Liste: Klassisch mit null-Zeiger
Zur Demonstration des Umgangs mit Verkettungen hier ein einfaches Beispiel einer
verketteten Liste.
public class MyLinkedList<E> {
private static class Node<E>{
E
data;
Node<E>
next;
}
Node(E data) {
this.data = data;
}
...
private int nodeCount = 0;
private Node<E> first;
first
public int size() {
return nodeCount;
}
Knoten
speichern die Informationen und einen
Verwies auf den nächsten Knoten
}
Seite 20
Datenstrukturen für Listen
Beispiel verkettete Liste: Klassisch mit null-Zeiger
Anhängen, Abfragen und Löschen eines Elements
public void add(E element) {
if (first == null) {
first = new Node<>(element);
} else {
Node<E> l = first;
while (l.next != null) {
l = l.next;
}
l.next = new Node<>(element);
}
nodeCount++;
}
public E get(int i) throws NoSuchElementException {
Node<E> l = first;
int j = 0;
while (i != j) {
if (l == null) {
throw new NoSuchElementException();
}
j++;
l = l.next;
}
if (l == null) {
throw new
NoSuchElementException();
}
return l.data;
}
public void delete (int i) throws NoSuchElementException {
if (first == null) {
throw new NoSuchElementException();
}
int j = 0;
Node<E> act = first; // points to the j-th element
Node<E> prev = null;
while (j < i && act != null) {
prev = act;
act = prev.next;
j++;
}
if (act == null) {
throw new NoSuchElementException();
} else {
nodeCount--;
if (prev != null) {
prev.next = act.next;
} else {
first = act.next;
}
}
}
Seite 21
Datenstrukturen für Listen
Beispiel verkettete Liste: Klassisch mit null-Zeiger
Die Liste soll iterierbar sein. Dazu muss sie das Interface java.util.Iterable implementieren
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyLinkedList<E> implements Iterable<E> {
private static class Node<E>{
E
data;
Node<E>
next;
}
Node(E data) {
this.data = data;
}
private int nodeCount = 0;
private Node<E> first;
. . .
}
@Override
public Iterator<E> iterator() {
// TODO Auto-generated method stub
return null;
}
Was muss diese Methode liefern?
Seite 22
Datenstrukturen für Listen
Beispiel verkettete Liste: Klassisch mit null-Zeiger
Die Liste soll iterierbar sein. Dazu muss sie das Interface java.util.Iterable implementieren
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
return false;
}
@Override
public E next() {
// TODO Auto-generated method stub
return null;
}
@Override
public void remove() {
// TODO Auto-generated method stub
}
}
};
Seite 23
Datenstrukturen für Listen
Beispiel verkettete Liste: Klassisch mit null-Zeiger
Die Liste soll iterierbar sein. Dazu muss sie das Interface java.util.Iterable implementieren
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
Node<E> actNode = first;
@Override
public boolean hasNext() {
return actNode != null;
}
@Override
public E next() {
if (actNode == null) {
throw new NoSuchElementException();
}
E value = actNode.data;
actNode = actNode.next;
return value;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
};
Seite 24
Vergleichen Sie die Funktionalität dieser
Methoden mit der der entsprechenden
Methoden von java.util.Iterator<E>
Algorithmen, Datentypen und Datenstrukturen
Algorithmen auf Listen
Algorithmen können „auf der Schnittstelle“ oder im „Inneren einer Liste“ agieren.
– Algorithmen auf dem Datentyp Liste
Beispiel Suchen: Die Suche verwendet die öffentlichen Methoden der Liste
– Algorithmen auf sequentiellen Datenstrukturen (Array / Verkettete-Liste)
Beispiel Suchen: Die Suche operiert auf den Datenstrukturen der Listen-Implementierung
public <T> boolean seach(List<T> l, T element) {
for (int i= 0; i<l.size(); i++) {
if (l.get(i).equals(element)) {
return true;
}
}
}
Suche in einer Liste:
Laufzeit: O(n) da schlimmstenfalls auf
alle n Elemente zugegriffen werden
muss.
return false;
Angenommen die Liste ist als verkette Liste implementiert – so wie
MyLinkedList oben – welche Komplexität hat der Algorithmus in
dieser Implementierung dann?
Seite 25
Algorithmen, Datentypen und Datenstrukturen
Algorithmen auf Listen
Algorithmen können „auf der Schnittstelle“ oder im „Inneren einer Liste“ agieren.
public <T> boolean seach(List<T> l, T element) {
for (int i= 0; i<l.size(); i++) {
if (l.get(i).equals(element)) {
return true;
}
}
}
return false;
Suche in einer Liste:
Laufzeit dieses Algorithmus': O(n): Für
den Zugriff l.get(i) wird konstante
Laufzeit angenommen.
Wenn die Liste als verkette Liste implementiert wurde, dann ist die Annahme O(1)
für l.get(i) falsch und damit die Annahme O(n) für die Suche ebenfalls.
Die sequentielle Suche als solche hat aber trotzdem die Komplexität O(n)!
Sie ist definiert unter der Voraussetzung, dass der Zugriff auf ein Element der
Liste O(1) ist. – Eine Annahmen, die sinnvollerweise implizit bei allen
Komplexitäts-Betrachtungen von Algorithmen auf Listen vorausgesetzt wird.
Seite 26
Algorithmen, Datentypen und Datenstrukturen
Algorithmen auf Listen
Algorithmen operieren auf Datentypen, hinter denen dann die Datenstrukturen der
Implementierung dieser Datentypen liegen
Beispiel Suchen:
public <T> boolean seach(MyLinkedList<T> l, T element) {
for (int i= 0; i<l.size(); i++) {
if (l.get(i).equals(element)) {
return true;
}
}
}
O(n) Operationen
auf der Liste (get)
return false;
public class MyLinkedList<E> {
. . .
public E get(int i) throws NoSuchElementException {
Node<E> l = first;
int j = 0;
while (i != j) {
if (l == null) { throw new NoSuchElementException(); }
j++;
l = l.next;
}
if (l == null) { throw new NoSuchElementException(); }
return l.data;
}
. . .
}
Seite 27
Jede get-Operation ist O(n)
Diese Suche ist O(n2).
Das liegt aber nicht an am
schlechten Suchalgorithmus,
sondern an einer für dieses
Einsatz ungeeigneten,
schlechten Datenstruktur.
Algorithmen, Datentypen und Datenstrukturen
Algorithmen und Datentypen / Datenstrukturen
Für Algorithmen sind Datenstrukturen interessant. Der Aufruf einer Methode eines Datentyps ist
„nichts weiter als“ ein Methodenaufruf. (Die Veranstaltung heißt darum auch „Datenstrukturen und
Algorithmen“)
Trotzdem spielen Datentypen eine wichtige Rolle :
Datentypen
– erfordern die Implementierung einer bestimmten Funktionalität.
– Diese wird durch Algorithmen realisiert,
– Deren Effizienz massiv davon beeinflusst wird, mit welchen Datenstrukturen der Datentyp
implementiert wurde.
Sequenz / Sequentielle Datenstruktur
– Datentypen und Datenstrukturen werden oft verwechselt oder identifiziert
Das ist teilweise berechtigt, verwirrt aber gelegentlich.
– Liste ist für uns ein Datentyp, bei dem unterschiedliche Datenstrukturen zur
Implementierung verwendet werden können
– Sequenz oder Sequenzielle Datenstruktur
Ein Datentyp mit der Eigenschaft dass
 Alle Elemente können über einen Index zugegriffen werden können
 der Zugriff auf jedes beliebige Element ist O(1)
Seite 28
Algorithmen auf Sequenzen
Sequenz / Sequenzielle Datenstruktur
– Datenstruktur (elementarer Datentyp)
– mit Listen-Operationen und
– mit Zugriffsoperationen der Komplexität O(1)
Algorithmen auf Sequenzen
– Suchen
finde ein Element (mit einer bestimmten Eigenschaft) in einer Sequenz
– Sortieren
erzeuge eine Permutation der Elemente, derart, dass ein Ordnungskriterium erfüllt ist.
– Muster erkennen
z.B. finde die längste gemeinsame Teilsequenz von zwei Sequenzen
...
Seite 29
Herunterladen