Document

Werbung
Grundlagen der Algorithmen
und Datenstrukturen
Kapitel 3.3-3.5
Prof. Dr. Christian Scheideler
Sequenzen
Sequenz:
s = <e0,…,en-1>
Arten, auf Element zuzugreifen:
• Feldrepräsentation: absoluter Zugriff über s[i]
s[0]: e0
s[1] s[2] s[3] s[4] s[5]
….
• Listenrepräsentation: relativer Zugriff über Nachfolger
und/oder Vorgänger
Nachf.
Nachf.
s
e0
e2
e1
Vorg.
Nachf.
Vorg.
Nachf.
e3
Vorg.
Vorg.
….
Doppelt verkettete Liste
Type Handle=Pointer to Item
Class Item of Element
e: Element
next: Handle
prev: Handle
…
e
e
Class List of Item
h: Handle
…weitere Variablen und Operationen…
Invariante: (this: Zeiger auf aktuelles Element)
next! prev = prev! next = this
e
…
Doppelt verkettete Liste
Einfache Verwaltung:
durch „Dummy“-Element mit Inhalt ?:
?
e1
e2
en
…
Anfangs:
?
Doppelt verkettete Liste
Zentrale Operation: splice
• splice entfernt <a,…,b> aus Sequenz und
fügt sie hinter einem t an.
• Bedingung: <a,…,b> muss eine Teilsequenz sein, b ist nicht vor a, und t darf
nicht in <a,…,b> sein.
Splice (<e1,…,a´,a,…,b,b´,…,t,t‘…,en>, a, b, t) =
<e1,…,a´,b´,…,t,a,…,b,t´,…,en>
Doppelt verkettete Liste
Procedure splice(a,b,t: Handle)
// schneide <a,…,b> heraus
a´ := a!prev
b´ := b!next
a´!next := b´
b´!prev := a´
// füge <a,…,b> hinter t ein
t´ := t!next
b!next := t´
a!prev := t
t!next := a
t´!prev := b
a´
a
b
b´
b
t´
…
…
…
…
t
a
…
…
…
…
Doppelt verkettete Liste
h: Handle auf
?-Item in List
?
e1
…
en
Function head(): Handle
return h
?
Function isEmpty: {0,1}
return h.next = h
Function first(): Handle
return h.next
Kann evtl. auf ?-Element zeigen!
Function last(): Handle
return h.prev
Kann evtl. auf ?-Element zeigen!
Doppelt verkettete Liste
h: Handle to
?-Item of List
?
e1
…
en
Procedure moveAfter(b,a: Handle)
splice(b,b,a)
// schiebe b nach a
Procedure moveToFront(b: Handle)
moveAfter(b,head())
// schiebe b nach vorne
Procudure moveToBack(b: Handle)
moveAfter(b,last())
// schiebe b nach hinten
Doppelt verkettete Liste
Löschen und Einfügen von Elementen:
mittels extra Liste freeList
h
b
…
…
Procedure remove(b:Handle)
moveAfter(b, freeList.head())
…
freeList
Procedure popFront()
remove(first())
Procedure popBack()
remove(last())
…
…
freeList: gut für Laufzeit, da Speicherallokation teuer
Doppelt verkettete Liste
Function insertAfter(x: Element, a: Handle): Handle
checkFreeList
a´ := freeList.first()
h
a
moveAfter(a´, a)
…
a´!e := x
…
…
return a´
a´
x
Function insertBefore(x: Element, b: Handle): Handle
return insertAfter(e, pred(b))
Procedure pushFront(x: Element) insertAfter(x, head())
Procedure pushBack(x: Element) insertAfter(x, last())
Doppelt verkettete Liste
Manipulation ganzer Listen:
last
h
Procedure concat(L: List)
// L darf nicht leer sein!
splice(L.first(), L.last(), last())
Procedure makeEmpty()
freeList.concat(this)
…
…
L
…
…
Doppelt verkettete Liste
Suche nach einem Element:
Trick: verwende „Dummy“-Element
x
e1
…
en
Function findNext(x:Element, from: Handle): Handle
h.e := x
while from! e <> x do
from := from!next
return from
Einfach verkettete Liste
Type SHandle=Pointer to SItem
Class SItem of Element
e: Element
…
next: Handle
e
e
Class SList of SItem
h: SHandle
…weitere Variablen und Operationen…
e
…
Einfach verkettete Liste
Procedure splice(a´, b, t: SHandle)
a´!next := b!next
t!next := a´!next
b!next := t!next
a´
t
a
…
b
b´
t´
Stacks und Queues
Grundlegende Datenstrukturen für Sequenzen:
• Stack
• FIFO-Queue:
Stack
Operationen:
• pushBack:
<e0,…,en>.pushBack(e) = <e0,…,en,e>
• popBack:
<e0,…,en>.popBack = <e0,…,en-1>
• last:
last(<e0,…,en>) = en
Implementierungen auf vorherigen Folien.
FIFO-Queue
Operationen:
• pushBack:
<e0,…,en>.pushBack(e) = <e0,…,en,e>
• popFront:
<e0,…,en>.popFront = <e1,…,en>
• first:
first(<e0,…,en>) = e0
Implementierungen auf vorherigen Folien
Beschränkte FIFO-Queue
Class BoundedFIFO(n: IN) of Element
b: Array[0..n] of Element
h=0: IN
// Index des ersten Elements
t=0: IN
// Index des ersten freien Eintrags
Function isEmpty(): {0,1}
return h=t
Function first(): Element
return b[h]
Function size(): IN
return (t-h+n+1) mod (n+1)
b
n
h
pop
0 1
t
2
3
. push
.
.
Beschränkte FIFO-Queue
Class BoundedFIFO(n: IN) of Element
b: Array[0..n] of Element
h=0: IN
// Index des ersten Elements
t=0: IN
// Index des ersten freien Eintrags
Procedure pushBack(x: Element)
assert size<n
h
b[t] := x
t := (t+1) mod (n+1)
pop
Procedure popFront()
assert (not isEmpty())
h := (h+1) mode (n+1)
b
n
0 1
t
2
3
. push
.
.
Fazit
• Listen sind sehr flexibel, wenn es darum
geht, Elemente in der Mitte einzufügen
• Felder können in konstanter Zeit auf jedes
Element zugreifen
• Listen haben kein Reallokationsproblem
bei unbeschränkten Größen
! beide Datenstrukturen einfach, aber oft
nicht wirklich zufriedenstellend
Beispiel: Sortierte Sequenz
Problem: bewahre nach jeder Einfügung und
Löschung eine sortierte Sequenz
1
3
10
14
19
3
5
10
14
19
1
3
5
10
19
insert(5)
1
remove(14)
Beispiel: Sortierte Sequenz
S: sortierte Sequenz
Jedes Element e identifiziert über Key(e).
Operationen:
• <e0,…,en>.insert(e) = <e0,…,ei,e,ei+1,…,en>
für das i mit Key(ei) < Key(e) < Key(ei+1)
• <e0,…,en>.remove(k) = <e0,…,ei-1,ei+1,…,en> für
das i mit Key(ei)=k
• <e0,…,en>.find(k) = ei für das i mit Key(ei)=k
Beispiel: Sortierte Sequenz
• Realisierung als Liste:
– insert, remove und find auf Sequenz der
Länge n kosten im worst case (n) Zeit
• Realisierung als Feld:
– insert und remove kosten im worst case
(n) Zeit
– find kann so realisiert werden, dass es im
worst case nur O(log n) Zeit benötigt
(! binäre Suche!)
Beispiel: Sortierte Sequenz
Kann man insert und remove besser mit
einem Feld realisieren?
• folge Beispiel der Bibliothek!
• verwende Hashtabellen (Kapitel 4)
Beispiel: Sortierte Sequenz
Bibliotheksprinzip: lass Lücken!
Angewandt auf sortiertes Feld:
1
3
10
1
3
5
10
1
3
5
10
14
19
14
19
insert(5)
remove(14)
19
Beispiel: Sortierte Sequenz
Durch geschickte Verteilung der Lücken:
amortierte Kosten für insert und remove (log2 n)
1
3
10
1
3
5
10
1
3
5
10
14
19
14
19
insert(5)
remove(14)
19
Nächste Woche
• Weiter mit Kapitel 4: Hashtabellen
• Midterm am 29. Mai!
• Anmeldung über Grundstudiumstool
Herunterladen