栈(Stack)是一种特殊的线性数据结构,遵循后进先出(Last In, First Out, LIFO)原则。想象一下一叠盘子,每次只能从最上面拿取或放置盘子,这就是栈的工作原理。栈在计算机科学中有广泛应用,如函数调用栈、表达式求值、括号匹配等。
基本概念
-
栈顶(Top):栈中最上面的元素,也是最近被压入栈的元素。对栈的所有操作(压入、弹出、读取栈顶元素)都围绕栈顶进行。
-
栈底(Bottom):栈中最下面的元素,通常固定不变。在实现上,栈底可以是一个固定的哨兵值或者一个空指针。
-
压栈(Push):将一个元素添加到栈顶。
-
弹栈(Pop):移除栈顶元素并返回其值。若栈为空,则弹栈操作通常会触发错误或返回特定值表示栈已空。
-
栈空(Empty Stack):当栈中没有元素时,称栈为空。
-
栈满(Full Stack):对于有固定容量的栈,当无法再添加新元素时,称栈为满。动态分配空间的栈通常不会出现栈满的情况。
-
栈的深度/大小(Stack Size/Depth):当前栈中元素的数量。
栈的抽象数据类型(ADT)
1// 抽象数据类型定义
2typedef struct Stack Stack;
3
4// 创建一个空栈
5Stack* createStack();
6
7// 销毁栈,并释放所有内存
8void destroyStack(Stack* stack);
9
10// 判断栈是否为空
11bool isEmpty(Stack* stack);
12
13// 返回栈的大小(元素数量)
14int size(Stack* stack);
15
16// 将元素 item 压入栈顶
17void push(Stack* stack, void* item);
18
19// 弹出栈顶元素并返回其值。若栈为空,行为未在此处定义(可能抛出异常或返回特定值)
20void* pop(Stack* stack);
21
22// 查看栈顶元素但不移除。若栈为空,行为未在此处定义
23void* peek(Stack* stack);
实现示例(基于数组)
1#include <stdio.h>
2#include <stdlib.h>
3
4#define STACK_SIZE 100
5
6typedef struct {
7 int items[STACK_SIZE];
8 int top;
9} Stack;
10
11Stack* createStack() {
12 Stack* stack = (Stack*) malloc(sizeof(Stack));
13 if (!stack) {
14 printf("Memory allocation failed.\n");
15 exit(1);
16 }
17 stack->top = -1; // 初始化为空栈
18 return stack;
19}
20
21void destroyStack(Stack* stack) {
22 free(stack);
23}
24
25bool isEmpty(Stack* stack) {
26 return stack->top == -1;
27}
28
29int size(Stack* stack) {
30 return stack->top + 1;
31}
32
33void push(Stack* stack, int item) {
34 if (stack->top == STACK_SIZE - 1) {
35 printf("Stack overflow.\n");
36 exit(1);
37 }
38 stack->items[++stack->top] = item;
39}
40
41int pop(Stack* stack) {
42 if (isEmpty(stack)) {
43 printf("Stack underflow.\n");
44 exit(1);
45 }
46 return stack->items[stack->top--];
47}
48
49int peek(Stack* stack) {
50 if (isEmpty(stack)) {
51 printf("Stack is empty.\n");
52 exit(1);
53 }
54 return stack->items[stack->top];
55}
56
57int main() {
58 Stack* stack = createStack();
59
60 push(stack, 1);
61 push(stack, 2);
62 push(stack, 3);
63
64 printf("Popped: %d\n", pop(stack));
65 printf("Top element: %d\n", peek(stack));
66
67 destroyStack(stack);
68
69 return 0;
70}
栈因其简单且高效的特性,在编程中常用于实现递归算法、回溯搜索、括号匹配、表达式求值、深度优先搜索(DFS)等场景。由于栈只允许在一端进行操作,其时间复杂度为O(1),即常数时间复杂度。