一、什么是栈?
栈(Stack)是一种后进先出(LIFO, Last In First Out)的线性数据结构,它只允许在一端进行插入和删除操作,这一端被称为栈顶(top) ,另一端则称为栈底(base)。
简单来说,栈就像一个 "一端封闭的容器":
- 最后放入的元素,会最先被取出;
- 最先放入的元素,会最后被取出。
二、栈的核心结构
视频中提到,栈的本质是一段连续内存 + 一个栈顶指针:
- 连续内存:用于存储栈中的元素,通常用数组实现;
- 栈顶指针(top):指向栈顶元素的位置,初始值为
-1,表示栈为空。
对应的 C 语言代码定义如下:
#define MAX_SIZE 100 // 栈的最大容量
// 栈的结构体定义
typedef struct {
int data[MAX_SIZE]; // 连续内存,存储栈元素
int top; // 栈顶指针,-1表示空栈
} Stack;
三、栈的基本操作
栈的核心操作有两个:入栈(Push)和出栈(Pop) ,这两个操作的时间复杂度都是 O(1)(常数时间),因为它们不需要遍历整个栈,只需要操作栈顶指针。
1. 入栈(Push):压入栈顶
入栈操作是将元素添加到栈顶,步骤如下:
- 判满 :如果栈顶指针等于
MAX_SIZE - 1,说明栈已满,无法再添加元素; - 先自增,再写入:将栈顶指针加 1,然后将元素写入到新的栈顶位置。
代码实现:
// 入栈操作
void push(Stack *s, int value) {
// 判满:栈顶指针到达最大容量的前一个位置
if (s->top == MAX_SIZE - 1) {
printf("栈已满,无法入栈!\n");
return;
}
// 先自增栈顶指针,再写入元素
s->data[++s->top] = value;
}
2. 出栈(Pop):弹出栈顶
出栈操作是将栈顶元素取出,步骤如下:
- 判空 :如果栈顶指针等于
-1,说明栈为空,无法再取出元素; - 先读取,再自减:先读取栈顶元素,然后将栈顶指针减 1。
代码实现:
// 出栈操作
int pop(Stack *s) {
// 判空:栈顶指针为-1表示空栈
if (s->top == -1) {
printf("栈为空,无法出栈!\n");
return -1; // 用-1表示错误,实际应用中可自定义错误码
}
// 先读取栈顶元素,再自减栈顶指针
return s->data[s->top--];
}
四、栈的初始化与建立
在使用栈之前,需要先初始化栈,将栈顶指针设置为 -1,表示栈为空。
代码实现:
// 初始化栈
void initStack(Stack *s) {
s->top = -1; // 栈顶指针归位到-1,表示空栈
}
建立栈的过程就是逐个将元素入栈的过程:
int main() {
Stack s;
initStack(&s); // 初始化栈
// 逐个入栈,建立栈
push(&s, 10);
push(&s, 20);
push(&s, 30);
// 出栈测试
printf("出栈元素:%d\n", pop(&s)); // 输出30(后进先出)
printf("出栈元素:%d\n", pop(&s)); // 输出20
printf("出栈元素:%d\n", pop(&s)); // 输出10
return 0;
}
五、栈溢出(Stack Overflow)
栈的容量是有限的,当入栈的元素超过栈的最大容量时,就会发生栈溢出。
视频中提到,当栈顶指针越过 MAX_SIZE - 1 时,栈空间就耗尽了,此时入栈操作会失败。
避免栈溢出的方法:
- 合理设置栈的最大容量
MAX_SIZE; - 在入栈前先判断栈是否已满。
六、总结
栈是一种非常基础且重要的数据结构,它的核心特点是后进先出 ,通过连续内存 + 栈顶指针 实现,基本操作的时间复杂度为 O(1)。
在实际应用中,栈常用于:
- 函数调用栈(保存函数的返回地址和局部变量);
- 表达式求值(如后缀表达式计算);
- 括号匹配问题;
- 深度优先搜索(DFS)等。
希望这篇文章能帮助你理解栈的原理和实现!