Stapel (Stack): LIFO, Operationen und Anwendungen
Du erklärst das Stapelprinzip, verfolgst push, pop und peek und lernst Anwendungen sowie den Umgang mit leeren und vollen Stapeln kennen.
Kurz gesagt
Ein Stack speichert Werte nach LIFO („Last In, First Out“): Der zuletzt abgelegte, noch vorhandene Wert kommt zuerst heraus. push legt einen Wert oben ab. Bei nicht leerem Stack liefert pop den obersten Wert und entfernt ihn; peek liefert ihn ohne Entfernen.
Ein Stack, auf Deutsch Stapel, ist eine Datenstruktur: eine festgelegte Art, Daten zu ordnen und auf sie zuzugreifen. Du legst Werte oben ab und greifst auch von oben auf sie zu. Damit kannst du Abläufe nachvollziehen, bei denen der letzte Schritt zuerst bearbeitet werden muss.
Deine Lernziele
Ein Stack arbeitet nach dem LIFO-Prinzip. LIFO steht für „Last In, First Out“, auf Deutsch „zuletzt hinein, zuerst heraus“. Von den noch vorhandenen Werten wird der zuletzt abgelegte zuerst entnommen. Nach seiner Entnahme ist der unmittelbar darunterliegende Wert oben.
Eine Warteschlange arbeitet nach dem FIFO-Prinzip. FIFO steht für „First In, First Out“, also „zuerst hinein, zuerst heraus“. Von den noch vorhandenen Werten wird der zuerst eingefügte zuerst entnommen. Stack und Warteschlange unterscheiden sich damit durch ihre Entnahmereihenfolge. Beide Regeln richten sich nach dem Einfügen, nicht nach der Größe eines Zahlenwerts.
LIFO-Prinzip
Der zuletzt abgelegte, noch vorhandene Wert wird zuerst entnommen. Einfügen und Entnehmen finden an der Oberseite des Stacks statt.
Beispiel: Drei beschriftete Karten einlegen
- 1Die Karten „Nord“, „Ost“ und „West“ werden in dieser Reihenfolge eingefügt.
- 2Ein Stack liefert beim vollständigen Entnehmen zuerst „West“, dann „Ost“, dann „Nord“.
- 3Eine Warteschlange liefert zuerst „Nord“, dann „Ost“, dann „West“.
Teste dich
Die Operationen eines Stacks sind festgelegte Aktionen. push legt einen Wert oben ab. Bei einem nicht leeren Stack liefert pop den obersten Wert zurück und entfernt ihn. peek liefert bei einem nicht leeren Stack den obersten Wert zurück, ohne ihn zu entfernen. Ein Rückgabewert ist das Ergebnis, das eine Operation an den aufrufenden Ablauf liefert. pop und peek unterscheiden sich somit darin, ob sie den gespeicherten Inhalt verändern.
Wir schreiben Stack-Zustände in eckigen Klammern: Bei [18, 35] liegt 18 unten und 35 oben. Die Oberseite steht rechts. [] bezeichnet den leeren Stack. push(x) bedeutet: Lege den Wert x oben ab. Bei pop() und peek() stehen keine Angaben in den runden Klammern, weil kein einzufügender Wert benötigt wird. Diese Schreibweisen beschreiben Operationen; sie sind kein vollständiges ausführbares Programm.
Die folgenden Abläufe verwenden pop() und peek() bei einem nicht leeren Stack. Den Leerfall legen wir im Abschnitt zur Implementierung ausdrücklich fest. Auch der Rückgabewert von push ist hier nicht festgelegt.
| Operation | Rückgabe | Änderung am Inhalt |
|---|---|---|
| push(x) | Hier nicht festgelegt | x kommt oben hinzu. |
| pop() bei nicht leerem Stack | Oberster Wert | Der oberste Wert wird entfernt. |
| peek() bei nicht leerem Stack | Oberster Wert | Der Inhalt bleibt gleich. |
Beispiel: Werte ablegen, lesen und entnehmen
- 1Start: Der Stack ist leer, also [].
- 2Nach push(31) und push(46) ist der Zustand [31, 46].
- 3peek() liefert 46. Der Zustand bleibt [31, 46].
- 4pop() liefert 46. Der Zustand ist danach [31].
- 5Ein weiteres pop() liefert 31. Danach ist der Zustand [].
Teste dich
Eine Anwendung ist das Speichern von Rücksprungadressen. Eine Rücksprungadresse gibt an, an welcher Stelle ein Programm nach einem Aufruf weitergehen soll. Ein Unterprogramm ist ein benannter Programmteil, der eine Aufgabe erledigt. Wir betrachten einen Ablauf, bei dem Aufrufe ihre Rücksprungadressen auf einem Stack ablegen. Ruft ein Unterprogramm ein weiteres auf, liegt dessen Rücksprungadresse oben. Beim Zurückkehren wird sie zuerst entnommen. Der aufrufende Programmteil läuft an der Stelle nach dem Aufruf weiter, nicht wieder an seinem Anfang.
Ein Stack kann auch Rechenausdrücke in Postfixnotation auswerten. Dabei stehen Zahlen vor dem zugehörigen Rechenzeichen. Die Folge „8 3 −“ bedeutet zum Beispiel 8-3=5. Lies die Folge von links nach rechts. Lege jede Zahl auf den Stack. Ein Rechenzeichen für zwei Zahlen nimmt zuerst den rechten, dann den linken Rechenwert herunter. Berechne linker Wert minus rechter Wert bei einer Subtraktion; ein Pluszeichen verlangt entsprechend eine Addition. Lege das Ergebnis wieder oben ab.
Wir betrachten gültige Folgen mit Plus- und Minuszeichen: Bei jedem Rechenzeichen müssen mindestens zwei Werte bereitliegen. Nach der vollständigen Auswertung bleibt genau ein Ergebnis auf dem Stack. Die Reihenfolge der Zahlen und Zeichen legt die Rechnung fest. Zusätzliche Klammern und Vorrangregeln wie „Punkt vor Strich“ werden für diese Schreibweise nicht benötigt.
Beispiel: Verschachtelte Programmaufrufe
- 1„Haupt“ ruft „Lesen“ auf. Die Stelle nach diesem Aufruf in „Haupt“ wird auf dem Stack gespeichert.
- 2„Lesen“ ruft „Umrechnen“ auf. Die Stelle nach diesem Aufruf in „Lesen“ kommt oben hinzu.
- 3Wenn „Umrechnen“ fertig ist, wird die obere Rücksprungadresse entnommen. „Lesen“ läuft nach seinem Aufruf weiter.
- 4Wenn anschließend „Lesen“ fertig ist, führt die nächste Rücksprungadresse zurück nach „Haupt“.
Merke: Bei einer Postfix-Subtraktion ist der zuerst entnommene Wert der rechte Rechenwert. Der danach entnommene Wert steht links vom Minuszeichen.
Teste dich
Eine Implementierung ist die konkrete Umsetzung der beschriebenen Regeln in einem Programm. Wir beschreiben ein Beispiel in Python, einer Programmiersprache. Es speichert die Stack-Werte in einer dynamisch erweiterbaren Liste. Das bedeutet: Die Liste kann beim Einfügen wachsen, soweit Speicher verfügbar ist. Ihr Ende dient als Oberseite. push hängt dort einen Wert an. Bei einem nicht leeren Stack entfernt pop den letzten Listeneintrag und liefert ihn zurück; peek liest ihn ohne Entfernen.
Vor dem Entfernen oder Lesen prüft dieses Beispiel, ob die Liste leer ist. Dann liefern pop und peek den besonderen Python-Wert None zurück und lassen den Stack leer. None bedeutet hier „kein Wert vorhanden“. Für unsere Abläufe speichern wir Zahlen. 0 ist dabei eine gültige Zahl und kein Zeichen für einen leeren Stack. [] beschreibt den leeren Listeninhalt; es ist hier nicht der Rückgabewert von pop oder peek. Würde man auch None als Eintrag zulassen, könnte man am Rückgabewert allein einen solchen Eintrag nicht vom Leerfall unterscheiden.
Diese Behandlung des Leerfalls ist eine Festlegung dieses Beispiels. Andere Implementierungen können einen Fehler melden. Auch eine erweiterbare Liste hat keinen unbegrenzten Speicher. Für eine zusätzliche Variante mit fester Platzanzahl legen wir fest: push prüft vor dem Einfügen, ob noch ein Platz frei ist. Ist der Stack voll, meldet die Operation „voll“ und lässt den Inhalt unverändert. Ein voller Stack ist dabei nicht dasselbe wie ein leerer Stack.
| Fall | Prüfung | Festgelegtes Verhalten |
|---|---|---|
| pop oder peek im Zahlenbeispiel | Ist die Liste leer? | Falls ja: None zurückgeben; Inhalt bleibt leer. |
| push bei fester Platzanzahl | Ist noch ein Platz frei? | Falls nein: „voll“ melden; Inhalt bleibt gleich. |
Merke: Die Stack-Regeln legen die Reihenfolge fest. Wie ein Programm einen leeren oder vollen Stack meldet, muss seine Implementierung zusätzlich festlegen.
Teste dich
Alles auf einen Blick
Stapel (Datenstruktur)
Ein Stack liefert den zuletzt abgelegten, noch vorhandenen Wert zuerst; eine Warteschlange den zuerst eingefügten.
push legt oben ab. Bei nicht leerem Stack liefern pop und peek den obersten Wert; pop entfernt ihn, peek nicht.
Rücksprungadressen und die Auswertung von Postfix-Ausdrücken können mit einem Stack verwaltet werden.
Im beschriebenen Python-Beispiel liefern pop und peek bei leerem Stack None.
Erweiterbarer Speicher ist begrenzt. Unsere Variante mit fester Platzanzahl weist push bei vollem Stack mit der Meldung „voll“ zurück.
Den Postfix-Ausdruck „14 6 2 − +“ auswerten
- 1Der Stack startet leer. Wir lesen von links nach rechts; rechts in den eckigen Klammern liegt oben.
- 2Die Zahlen 14, 6 und 2 werden nacheinander abgelegt. Der Zustand ist [14, 6, 2].
- 3Beim Minuszeichen entnehmen wir zuerst 2 als rechten und dann 6 als linken Wert. Wir rechnen 6-2=4 und legen 4 ab. Der Zustand ist [14, 4].
- 4Beim Pluszeichen entnehmen wir zuerst 4 und dann 14. Wir rechnen 14+4=18 und legen 18 ab. Der Zustand ist [18].
- 5Die Folge ist vollständig gelesen. Der einzelne verbleibende Wert 18 ist das Ergebnis.
Fehler finden
Fehler in Zeile 3Die letzte Zeile behandelt peek fälschlich wie eine Entnahme. Richtig: Nach peek() bleibt der Zustand [13, 28], weil peek den obersten Wert nicht entfernt.
Lückentext
Du legst zuerst 16 und dann 29 auf einen leeren Stack. Ohne weitere Operationen liefert pop() anschließend . Ein Stack enthält unten 5 und oben 22. Nach peek() beträgt die Anzahl gespeicherter Werte . Im beschriebenen Python-Zahlenbeispiel ist der Stack leer. pop() liefert dort .