1. 栈的基本概念与特性解析
栈(Stack)是计算机科学中最基础且重要的数据结构之一,它遵循"后进先出"(LIFO, Last In First Out)的原则。想象一下餐厅里叠放的餐盘——你总是取走最上面的那个盘子,新洗好的盘子也会放在最上面。这种特性使得栈在程序设计中有着不可替代的作用。
1.1 栈的核心特性
栈作为一种受限的线性表,与普通线性表相比有两个关键区别:
- 操作受限性:只能在栈顶进行插入(入栈)和删除(出栈)操作
- 无查找功能:不能像数组那样通过索引直接访问任意位置的元素
栈的两个关键位置:
- 栈顶(Top):允许进行插入和删除操作的一端
- 栈底(Bottom):固定不变的一端,不允许直接操作
注意:栈的"指针"实际上是一个标记栈顶位置的变量,在顺序栈中通常是数组下标,在链式栈中则是结点指针。
1.2 栈的典型应用场景
- 函数调用栈:每次函数调用时,系统会将返回地址、参数和局部变量压入栈中;函数返回时再依次弹出
- 表达式求值:处理运算符优先级和中缀表达式转后缀表达式
- 撤销操作(Undo):文字编辑器和图像处理软件中的撤销功能
- 括号匹配检查:编译器检查代码中的括号是否成对出现
- 递归实现:递归函数本质上就是利用系统栈实现的
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 顺序栈的实现与优化
顺序栈使用数组作为底层存储结构,通过一个变量(通常称为top)来标记栈顶位置。根据top指向的不同,有两种常见的实现方式。
2.1 实现方式一:top指向栈顶元素
c复制#define MAX_SIZE 100 // 栈的最大容量
typedef struct {
int data[MAX_SIZE];
int top; // 栈顶指针
} SeqStack;
// 初始化栈
void InitStack(SeqStack *S) {
S->top = -1; // 初始化为-1表示空栈
}
// 判空
int IsEmpty(SeqStack *S) {
return S->top == -1;
}
// 判满
int IsFull(SeqStack *S) {
return S->top == MAX_SIZE - 1;
}
// 入栈
void Push(SeqStack *S, int value) {
if (IsFull(S)) {
printf("栈已满,无法入栈\n");
return;
}
S->data[++S->top] = value; // 先移动top再赋值
}
// 出栈
int Pop(SeqStack *S) {
if (IsEmpty(S)) {
printf("栈为空,无法出栈\n");
return -1; // 错误码
}
return S->data[S->top--]; // 先取值再移动top
}
// 获取栈顶元素
int GetTop(SeqStack *S) {
if (IsEmpty(S)) {
printf("栈为空\n");
return -1;
}
return S->data[S->top];
}
实现细节解析:
top初始化为-1,表示栈为空- 入栈时先移动
top再赋值,保证top始终指向栈顶元素 - 出栈时先取值再移动
top,确保不会丢失数据 - 判满条件是
top == MAX_SIZE - 1,因为数组下标从0开始
2.2 实现方式二:top指向栈顶的下一个位置
c复制typedef struct {
int data[MAX_SIZE];
int top; // 指向下一个入栈位置
} SeqStack2;
void InitStack2(SeqStack2 *S) {
S->top = 0; // 初始化为0
}
int IsEmpty2(SeqStack2 *S) {
return S->top == 0;
}
int IsFull2(SeqStack2 *S) {
return S->top == MAX_SIZE;
}
void Push2(SeqStack2 *S, int value) {
if (IsFull2(S)) {
printf("栈已满\n");
return;
}
S->data[S->top++] = value; // 先赋值再移动top
}
int Pop2(SeqStack2 *S) {
if (IsEmpty2(S)) {
printf("栈为空\n");
return -1;
}
return S->data[--S->top]; // 先移动top再取值
}
int GetTop2(SeqStack2 *S) {
if (IsEmpty2(S)) {
printf("栈为空\n");
return -1;
}
return S->data[S->top - 1]; // top-1才是栈顶元素
}
两种实现方式的对比:
| 特性 | 方式一 (top指向元素) | 方式二 (top指向下一个位置) |
|---|---|---|
| 初始化top值 | -1 | 0 |
| 判空条件 | top == -1 | top == 0 |
| 判满条件 | top == MAX_SIZE-1 | top == MAX_SIZE |
| 入栈操作 | data[++top] = value | data[top++] = value |
| 出栈操作 | value = data[top--] | value = data[--top] |
| 获取栈顶 | data[top] | data[top-1] |
实际开发中,方式二更为常见,因为它与很多其他数据结构的索引方式一致(如C++ STL中的stack实现)
3. 链式栈的设计与实现
链式栈使用链表作为存储结构,通常采用带头结点的单链表实现。与顺序栈相比,链式栈的最大优势是没有容量限制(理论上只受内存大小限制),但每个元素需要额外的指针空间。
3.1 链式栈的基本实现
c复制typedef struct StackNode {
int data;
struct StackNode *next;
} StackNode;
typedef struct {
StackNode *top; // 栈顶指针
int size; // 栈当前大小(可选)
} LinkedStack;
// 初始化栈
void InitLinkedStack(LinkedStack *S) {
S->top = NULL;
S->size = 0;
}
// 判空
int IsEmptyLinked(LinkedStack *S) {
return S->top == NULL;
}
// 入栈(头插法)
void PushLinked(LinkedStack *S, int value) {
StackNode *newNode = (StackNode*)malloc(sizeof(StackNode));
if (!newNode) {
printf("内存分配失败\n");
exit(1);
}
newNode->data = value;
newNode->next = S->top;
S->top = newNode;
S->size++;
}
// 出栈
int PopLinked(LinkedStack *S) {
if (IsEmptyLinked(S)) {
print
