Stack mit Doubly Linked List
Aufgabe
So funktioniert ein Stack auf einer doppelt verketteten Liste
- Stapel: Ein Stack funktioniert nach dem LIFO-Prinzip (Last In, First Out): Was zuletzt hineinkommt, kommt als Erstes wieder heraus. Zugänglich ist immer nur das oberste Element.
- Oben =
tail: Das oberste Element ist der letzte Knoten der Liste (tail), das unterste der erste (head). Ein leerer Stack ist eine leere Liste:headundtailsindNULL. - push: legt eine Zahl oben auf den Stack, also ans Ende der Liste – genau wie
insert_endin der vorigen Aufgabe: neuer Knoten mitprev = tailundnext = NULL; der bisher oberste Knoten zeigt mitnextauf ihn (bei leerem Stack stattdessenhead); danach zeigttailauf ihn. - pop: entfernt den obersten Knoten.
tailrückt überprevauf den Knoten darunter; dessennextwirdNULL. War es der einzige Knoten, werdenheadundtailNULL. Danach wird der entfernte Knoten mitfreefreigegeben. Weil jeder Knoten seinen Vorgänger kennt, musspopdafür nicht die ganze Liste durchlaufen. - top: liefert den Wert des obersten Knotens (
tail->value), ohne etwas zu ändern. - Ausgabe: von oben nach unten, also von
tailüberprevbisNULL.
Beispiel: Nach push 5, push 9 und push 2 sieht die Liste so aus (head → 5, tail → 2):
| Knoten | prev | next |
|---|---|---|
| 5 | NULL | 9 |
| 9 | 5 | 2 |
| 2 | 9 | NULL |
Der Stack von oben nach unten ist 2 9 5. pop entfernt 2: tail zeigt jetzt auf 9, next von 9 wird NULL, Knoten 2 wird freigegeben.
| Knoten | prev | next |
|---|---|---|
| 5 | NULL | 9 |
| 9 | 5 | NULL |
Jetzt liefert top den Wert 9, und der Stack von oben nach unten ist 9 5.
Demo
So sieht ein Lauf des fertigen Programms aus (hinter > und den Fragen stehen die Eingaben):
$ ./stack
(1) push (Element auf Stack legen)
(2) Stack ausgeben
(3) pop (oberstes Element entfernen)
(4) top (oberstes Element anzeigen)
(5) Stack löschen
(0) Programm beenden
> 1
Zahl: 5
> 1
Zahl: 9
> 2
Stack (oben -> unten): 9 5
> 3
Pop: 9
> 4
Top: 5
> 5
Stack geleert.
> 0
Programm beendet.Aufgabenmaterial
Für diese Aufgabe werden Sie ein von uns zur Verfügung gestelltes Codegerüst vervollständigen. Legen Sie einen Ordner stack an und darin die Datei stack.c mit diesem Inhalt:
Codegerüst
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int main(void)
{
// Menü nur einmal ausgeben
printf(
"(1) push (Element auf Stack legen)\n"
"(2) Stack ausgeben\n"
"(3) pop (oberstes Element entfernen)\n"
"(4) top (oberstes Element anzeigen)\n"
"(5) Stack löschen\n"
"(0) Programm beenden\n"
);
while (true)
{
// Benutzerauswahl abfragen
int choice = get_int("> ");
// Operationen auswerten
switch (choice)
{
case 1:
// Zahl auf den Stack legen
break;
case 2:
// Stack ausgeben
break;
case 3:
// oberstes Element entfernen
break;
case 4:
// oberstes Element anzeigen
break;
case 5:
// gesamten Stack löschen
break;
case 0:
// gesamten Stack löschen
printf("Programm beendet.\n");
return 0;
default:
printf("Ungültige Auswahl.\n");
}
}
}Spezifikation
Datenstruktur. Definieren Sie dieselbe Struktur node wie in Doubly Linked Lists und zwei globale Pointer auf den ersten und den letzten Knoten:
typedef struct node
{
int value;
struct node *next;
struct node *prev;
} node;
node *head = NULL; // zeigt auf den ersten Knoten (unten)
node *tail = NULL; // zeigt auf den letzten Knoten (oben)
Funktionen. Die Funktionen geben selbst nur dort etwas aus, wo es in der Tabelle steht; alle anderen Meldungen gibt main aus.
| Funktion | Aufgabe |
|---|---|
void push(int value) | Legt mit malloc einen neuen Knoten an und hängt ihn ans Ende der Liste (Regel 3), auch wenn der Stack leer ist. |
void pop(void) | Entfernt den obersten Knoten und gibt ihn mit free frei (Regel 4). Ist der Stack leer, tut die Funktion nichts. |
int top(void) | Gibt den Wert des obersten Knotens zurück, ohne den Stack zu ändern. Ist der Stack leer, gibt sie -1 zurück. |
bool is_empty(void) | Gibt true zurück, wenn der Stack leer ist, sonst false. |
void print_stack(void) | Gibt Stack (oben -> unten): aus, dann die Werte von tail bis head, durch Leerzeichen getrennt, dann einen Zeilenumbruch. |
void clear_stack(void) | Gibt alle Knoten mit free frei und setzt head und tail auf NULL. |
Weil -1 auch ein gewöhnlicher Wert auf dem Stack sein kann, prüft main vor top und pop mit is_empty, ob der Stack leer ist.
Menü. main gibt das Menü einmal aus und fragt dann immer wieder mit > nach einer Auswahl:
| Auswahl | Eingabe | Ausgabe |
|---|---|---|
1 | Zahl: | – |
2 | – | Stack (oben -> unten): 9 5 |
3 | – | Pop: 9 (erst ausgeben, dann entfernen) oder bei leerem Stack Stack ist leer. |
4 | – | Top: 5 oder bei leerem Stack Stack ist leer. |
5 | – | Stack geleert. |
0 | – | Programm beendet. – vorher wird der Stack freigegeben |
| sonst | – | Ungültige Auswahl. |
Die Zahlen in der Ausgabe sind Beispiele; eingesetzt werden jeweils die Werte auf dem Stack.
Hilfestellung
Klicken Sie auf die folgenden Tipps, um einige Ratschläge zu erhalten. Versuchen Sie aber zunächst, selbst so weit wie möglich zu kommen.
Was aus Doubly Linked Lists lässt sich übernehmen?
node,headundtailsind dieselben wie in Doubly Linked Lists, ebenso der Aufbau vonmainmitswitch.pushistinsert_endunter anderem Namen.print_stackistprint_backwardmit anderem Text am Zeilenanfang.- Neu sind nur
pop,topundis_empty. Wie ein Stack grundsätzlich funktioniert, zeigt das Short Stacks.
Wie in der vorigen Aufgabe gilt: Wenn Sie in einem case eine Variable anlegen (etwa int value = get_int("Zahl: ");), setzen Sie den Inhalt des case in geschweifte Klammern { … }.
Implementierung von pop
Merken Sie sich den obersten Knoten in einem Hilfs-Pointer, bevor Sie tail verschieben – sonst können Sie ihn danach nicht mehr freigeben:
node *tmp = tail;
tail = tail->prev;Jetzt gibt es zwei Fälle: Ist tail nicht NULL, ist es der neue oberste Knoten, und sein next muss NULL werden. Ist tail NULL, war tmp der einzige Knoten, und auch head muss NULL werden. Zum Schluss free(tmp).
Implementierung von clear_stack
Sie können die Knoten wie clear_list in der vorigen Aufgabe einzeln durchlaufen und freigeben. Kürzer geht es mit den Funktionen, die Sie schon haben: Rufen Sie pop auf, solange der Stack nicht leer ist.
Testen
Stellen Sie beim Testen Ihres Codes sicher, dass dieser mit folgenden Szenarien umgehen kann:
- Ausgabe, pop und top bei leerem Stack (
Stack (oben -> unten):bzw.Stack ist leer.) - mehrere push; die Ausgabe zeigt die zuletzt eingegebene Zahl zuerst
- pop bis zum letzten Element, danach erneut push – so fallen falsch gesetzte
head- undtail-Pointer auf - top ändert den Stack nicht: Zweimal hintereinander top liefert denselben Wert
- Löschen des gesamten Stacks (
5) und danach erneutes push - eine ungültige Menünummer
Korrektheit
Führen Sie in Ihrem Terminal den folgenden Befehl aus, um die Korrektheit Ihrer Arbeit zu überprüfen:
check50 -l inf-zone/exercises/2026/stackSpeicher
Ob Ihr Programm allen Speicher freigibt, zeigt valgrind (siehe Vorlesung 4). Legen Sie ein paar Zahlen auf den Stack, entfernen Sie einige und beenden Sie das Programm mit 0:
valgrind ./stackStyle
Führen Sie den folgenden Befehl aus, um den Stil Ihres Codes mit style50Prüfprogramm von CS50, das zeigt, wo Ihr Code von den Formatierungsregeln abweicht. In Grün zeigt es, was Sie ergänzen sollten, in Rot, was wegfallen soll. Glossar → zu analysieren:
style50 stack.cAbgeben
Geben Sie im Ordner stack ab:
inf upload stackDanach sehen Sie, welche Tests Ihr Programm besteht und ob die Übung für die Bonuspunkte zählt. Wie Sie inf installieren und sich anmelden, steht unter Abgeben mit inf.
Diese Seite als Markdown: ansehen herunterladen