Data Structure - Stack
A stack is a linear data structure that follows the Last In First Out (LIFO) principle. It can be vividly understood as a stack of plates — you can only place one on top, and you can only take one from the top.
Stack Concept and Principle
LIFO verification: push 10→20→30, pop 30→20→10 (last in, first out) | The stack only cares about the top, not the middle
Array Implementation (Sequential Stack)
Storage: fixed-size array + top pointer
push:items[++top] = value
pop:return items[top--]
Advantages: fast access, cache-friendly, simple implementation
Disadvantages: fixed capacity, may overflow
Linked List Implementation (Linked Stack)
Storage: dynamic nodes + top pointer (linked list head)
push: head insertion method newNode->next = top
pop: top = top->next; free(old head)
Advantages: no capacity limit, flexible dynamic expansion
Disadvantages: extra pointer overhead, cache-unfriendly
Array Implementation of Stack
Example
#include <stdbool.h> /* bool type */
#define MAX_SIZE 100 /* Maximum capacity of the stack */
/* Stack structure: contains data array and top pointer */
struct Stack {
int items[MAX_SIZE]; /* Array storing stack elements */
int top; /* Top pointer, -1 means the stack is empty */
};
/* Initialize stack: set top to -1 */
void initStack(struct Stack* s) {
s->top = -1;
}
/* Check empty: stack is empty when top is -1 */
bool isEmpty(struct Stack* s) {
return s->top == -1;
}
/* Check full: stack is full when top reaches capacity limit -1 */
bool isFull(struct Stack* s) {
return s->top == MAX_SIZE - 1;
}
/* Push: place an element on top of the stack
First move top, then write the data */
void push(struct Stack* s, int value) {
if (isFull(s)) {
printf("Stack is full, cannot push!\n");
return;
}
s->items[++s->top] = value; /* top increments by 1 first, then assign value */
printf("Push: %d\n", value);
}
/* Pop: remove and return the top element
First retrieve the data, then move top */
int pop(struct Stack* s) {
if (isEmpty(s)) {
printf("Stack is empty, cannot pop!\n");
return -1; /* Return a special value to indicate an error */
}
return s->items[s->top--]; /* Return the value first, then decrement top */
}
/* Peek: does not pop, only views the top */
int peek(struct Stack* s) {
if (isEmpty(s)) {
printf("Stack is empty!\n");
return -1;
}
return s->items[s->top];
}
int main() {
struct Stack s;
initStack(&s);
push(&s, 10); /* Push: 10 */
push(&s, 20); /* Push: 20 */
push(&s, 30); /* Push: 30 */
printf("Top element: %d\n", peek(&s)); /* Output: top element: 30 */
printf("Pop: %d\n", pop(&s)); /* Output: pop: 30 (last in, first out) */
printf("Pop: %d\n", pop(&s)); /* Output: pop: 20 */
printf("Pop: %d\n", pop(&s)); /* Output: pop: 10 */
if (isEmpty(&s)) {
printf("Stack is empty\n"); /* Output: stack is empty */
}
return 0;
}
Linked List Implementation of Stack
Example
#include <stdlib.h>
#include <stdbool.h>
/* Linked list node */
struct Node {
int data;
struct Node* next;
};
/* Push: uses the linked list head as the stack top, head insertion method
Time complexity: O(1) */
struct Node* push(struct Node* top, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = top; /* New node points to the original stack top */
printf("Push: %d\n", value);
return newNode; /* New node becomes the new stack top */
}
/* Pop: remove the head node of the linked list
Time complexity: O(1) */
struct Node* pop(struct Node* top) {
if (top == NULL) {
printf("Stack is empty!\n");
return NULL;
}
struct Node* temp = top;
printf("Popped: %d\n", top->data);
top = top->next; /* Move top back */
free(temp); /* Free old top */
return top;
}
/* Check if empty */
bool isEmpty(struct Node* top) {
return top == NULL;
}
int main() {
struct Node* stackTop = NULL; /* Initially stack is empty */
stackTop = push(stackTop, 100); /* Push: 100 */
stackTop = push(stackTop, 200); /* Push: 200 */
stackTop = push(stackTop, 300); /* Push: 300 */
printf("Top: %d\n", stackTop->data); /* Output: Top: 300 */
stackTop = pop(stackTop); /* Pop: 300 */
stackTop = pop(stackTop); /* Pop: 200 */
stackTop = pop(stackTop); /* Pop: 100 */
if (isEmpty(stackTop)) {
printf("Stack is empty\n"); /* Output: Stack is empty */
}
return 0;
}
The advantage of implementing a stack with a linked list is that theoretically there is no fixed capacity limit, limited only by the system's available memory. However, each node requires extra pointer storage space.
Stack Application Scenarios
| Application scenarios | Description | Why use stack |
|---|---|---|
| Function call stack | Save function return addresses and local variables | Function calls are nested, naturally LIFO |
| Bracket matching | Check whether brackets in code are correctly paired | Push left bracket onto stack, pop right bracket when it matches the top of stack |
| Undo operation | Undo feature in text editors and browsers | Each operation is pushed onto stack, popped when undoing |
| Expression evaluation | Evaluate infix/postfix expressions | Operands are pushed onto stack, result is pushed after operator computation |
| Depth-first search | DFS traversal algorithm for graphs | Explicitly use stack or recursion (implicitly using call stack) |