Stapel (Stack): LIFO, Operationen und Anwendungen
Ein Stapel, auch Stack oder Kellerspeicher, speichert Elemente nach dem LIFO-Prinzip: Das zuletzt abgelegte Element wird zuerst entnommen. Du arbeitest dabei nur am oberen Ende des Stapels.
Hake ab, was du schon kannst — und komm am Ende hierher zurück!
Das LIFO-Prinzip erkennen
Stell dir einen Stapel aus Umzugskisten vor. Eine neue Kiste stellst du oben ab. Willst du eine Kiste entnehmen, erreichst du ebenfalls zuerst die oberste. Eine weiter unten liegende Kiste wird erst zugänglich, nachdem du alle Kisten über ihr entfernt hast.
LIFO-Prinzip
LIFO bedeutet Last In, First Out: Das zuletzt eingefügte Element verlässt den Stapel als erstes.
Die Wörter „oben“ und „unten“ beschreiben die logische Ordnung. Ein gezeichneter Stack kann nach oben, nach unten oder zur Seite wachsen, ohne dass sich sein Verhalten ändert.
Ein Stack ist ein abstrakter Datentyp: Seine erlaubten Operationen und deren Wirkung werden festgelegt, ohne eine bestimmte technische Implementierung vorzuschreiben. Er kann beispielsweise mit einem Array oder einer verketteten Liste umgesetzt werden.
Nicht die Zeichnung und nicht die Speicheradresse bestimmen einen Stack, sondern der beschränkte Zugriff am oberen Ende und die LIFO-Reihenfolge.
Mit push, pop und peek arbeiten
Drei Operationen bilden den Kern eines Stacks:
push(x)legt das Elementxoben auf den Stack.pop()liefert das oberste Element und entfernt es.peek()odertop()liefert das oberste Element, entfernt es aber nicht.
Häufig gibt es zusätzlich eine Prüfung wie is_empty(), die feststellt, ob der Stack leer ist.
Ein leerer Stack erhält nacheinander diese Operationen:
push("A")— oben liegtA.push("B")— oben liegt nunB, darunterA.push("C")— oben liegt nunC, darunterBundA.peek()liefertC. Der Stack bleibt unverändert.pop()liefert und entferntC.- Ein weiteres
pop()liefert und entferntB.
Am Ende liegt nur noch A im Stack. Die Entnahmereihenfolge C, B, A ist die umgekehrte Reihenfolge des Einfügens.
pop und peek dürfen nicht verwechselt werden: Beide lesen das oberste Element, aber nur pop verändert den Stack.
Operationsfolgen Schritt für Schritt verfolgen
Bei längeren Folgen hilft eine Zustandsliste. Notiere die Elemente stets in derselben Richtung, zum Beispiel von unten nach oben. Ändere nur das obere Ende.
Mit einem leeren Stack wird ein kurzer Text aufgebaut:
push("sein!")push("dir")push("mit")push("Macht")push("die")push("Möge")
Von unten nach oben lautet der Stack nun:
sein!, dir, mit, Macht, die, Möge
Drei pop()-Operationen liefern nacheinander Möge, die, Macht. Im Stack bleiben sein!, dir und mit; mit liegt jetzt oben.
So vermeidest du typische Fehler
- Markiere eindeutig, welches Ende oben ist.
- Führe die Operationen in der angegebenen Reihenfolge aus.
- Schreibe bei
popzuerst den Rückgabewert auf und entferne ihn dann aus deinem Zustandsbild. - Verändere den Stack bei
peeknicht.
Anwendungen und Grenzen beurteilen
Ein Stack passt zu Aufgaben, bei denen der jüngste gespeicherte Zustand zuerst wieder gebraucht wird.
Browser und Rücksprünge
Bei einer vereinfachten Zurück-Funktion wird die zuvor besuchte Seite auf einem Stack gespeichert. Beim Zurückgehen wird die zuletzt gespeicherte Seite zuerst wieder aufgerufen.
Auch verschachtelte Unterprogramm- oder Methodenaufrufe folgen diesem Muster. Zu einem Aufruf wird eine Rücksprungadresse gespeichert. Ruft das Unterprogramm ein weiteres Unterprogramm auf, kommt dessen Rücksprungadresse obenauf. Der zuletzt begonnene Aufruf muss zuerst enden, damit die Rückkehrreihenfolge stimmt.
Ausdrücke und verschachtelte Strukturen
Stacks können Operanden und Operatoren bei der Auswertung mathematischer Ausdrücke zwischenspeichern. Ebenso helfen sie dabei, korrekt verschachtelte Strukturen zu prüfen: Eine zuletzt geöffnete Struktur muss zuerst wieder geschlossen werden.
Stack oder Warteschlange?
Eine Warteschlange entnimmt das älteste Element zuerst. Dieses Prinzip heißt FIFO: First In, First Out. Beim Stack wird dagegen das jüngste Element zuerst entnommen.
| Situation | Passende Ordnung | Begründung |
|---|---|---|
| Verschachtelte Funktionsaufrufe beenden | Stack, LIFO | Der zuletzt begonnene Aufruf endet zuerst. |
| Personen in Ankunftsreihenfolge bedienen | Warteschlange, FIFO | Die zuerst angekommene Person kommt zuerst an die Reihe. |
| Eine Zeichenfolge umkehren | Stack, LIFO | Die zuletzt gespeicherten Zeichen erscheinen zuerst. |
Einen Stack implementieren und Randfälle behandeln
Die abstrakten Operationen bleiben gleich, auch wenn die interne Darstellung wechselt.
Umsetzung mit einem Array
Ein Array kann die Elemente zusammenhängend speichern. Eine Zählvariable gibt an, wie viele Plätze belegt sind. push schreibt an die nächste freie Stelle und erhöht die Zahl der Elemente. pop verringert sie und liefert das zuvor oberste Element.
Ist das Array voll, muss die Implementierung den Stack vergrößern oder den Einfügeversuch eindeutig ablehnen. Bei dynamischer Vergrößerung können einzelne Operationen wegen des Kopierens länger dauern. Bei üblichen Implementierungen besitzen push und pop im Normalfall konstanten Aufwand, also $O(1)$.
Umsetzung mit einer verketteten Liste
Bei einer einfach verketteten Liste verweist top auf den obersten Knoten. Jeder Knoten enthält einen Wert und einen Verweis auf den darunterliegenden Knoten.
Für push(x):
- Erzeuge einen neuen Knoten mit dem Wert
x. - Lass ihn auf den bisherigen obersten Knoten verweisen.
- Setze
topauf den neuen Knoten.
Für pop():
- Prüfe zuerst, ob
topleer ist. - Merke den Wert des obersten Knotens.
- Setze
topauf dessen Nachfolger. - Liefere den gemerkten Wert.
Diese Reihenfolge behandelt auch einen Stack mit genau einem Element: Dessen Nachfolger ist leer, also wird der Stack nach pop() korrekt leer.
Unterlauf und Überlauf
Ein pop() oder peek() auf einem leeren Stack verursacht einen Stack-Unterlauf, wenn dieser Fall nicht abgefangen wird. Eine Implementierung muss dafür ein eindeutiges Verhalten festlegen, etwa einen Fehler auszulösen oder ausdrücklich einen besonderen Rückgabewert zu verwenden. Ein regulär speicherbarer Wert wie 0 eignet sich nicht ohne zusätzliche Kennzeichnung als Fehlersignal.
Ein Stack-Überlauf entsteht, wenn mehr Speicher benötigt wird, als für den Stack verfügbar ist. Das kann beispielsweise bei sehr tiefer Verschachtelung oder Rekursion geschehen.
Prüfe den Leerfall, bevor du auf das oberste Element zugreifst. Sonst findet der fehlerhafte Zugriff bereits vor der eigentlichen Fehlerbehandlung statt.
Karteikasten
Überlege zuerst selbst und drehe die Karte anschließend zum Prüfen um.
Alles auf einen Blick
- Stapel
- Prinzip
- LIFO: jüngstes Element zuerst
- Zugriff nur am oberen Ende
- Operationen
- push: auflegen
- pop: liefern und entfernen
- peek: nur betrachten
- Implementierung
- Array mit belegter Größe
- verkettete Liste mit top-Verweis
- Anwendungen
- Rücksprungadressen
- Browser-Zurücknavigation
- Ausdrucks- und Verschachtelungsprüfung
- Randfälle
- leerer Stack: Unterlauf
- begrenzter Speicher: Überlauf
- Prinzip
Abschluss-Check
Du beherrschst den Kern des Stacks, wenn du bei jeder Operationsfolge zuerst das obere Element bestimmst, peek und pop auseinanderhältst und den Leerfall ausdrücklich prüfst.
Mit Google fortfahren