栈(Stack)

1218 字
6 分钟
栈(Stack)
Warning

含AI生成内容

栈(Stack)#

定义#

(Stack)是只允许在一端进行插入和删除操作的线性表。允许操作的一端称为栈顶(top),另一端称为栈底(bottom)。

核心特性:后进先出(Last In First Out, LIFO)。

┌───┐ ← 栈顶(插入/删除端)
│ e₃│
├───┤
│ e₂│
├───┤
│ e₁│
└───┘ ← 栈底

基本操作#

操作说明时间复杂度
InitStack(&S)初始化空栈O(1)
Push(&S, e)入栈(栈顶插入)O(1)
Pop(&S, &e)出栈(栈顶删除)O(1)
GetTop(S, &e)读栈顶元素O(1)
StackEmpty(S)判空O(1)

易错:栈是”操作受限的线性表”,不是”特殊的存储结构”。栈描述的是逻辑结构上的操作限制,底层可以用顺序存储(顺序栈)或链式存储(链栈)实现。

卡特兰数(出栈序列计数)#

n 个不同元素依次入栈,合法的出栈序列总数为卡特兰数

C(n)=C(2n,n)n+1C(n) = \frac{C(2n, n)}{n + 1}
n合法序列数
11
22
35
414
542

408 选择题常问”以下哪个不是合法的出栈序列”,可用卡特兰数验证总数,也可逐步模拟判断。


顺序栈#

存储结构#

#define MaxSize 50
typedef struct {
int data[MaxSize];
int top; // 栈顶指针
} SqStack;

栈顶指针的两种约定(⭐选择题陷阱)#

约定初始值栈空栈满入栈出栈
top 指向栈顶元素top = -1top == -1top == MaxSize-1++top,再赋值先取值,再 top--
top 指向栈顶下一个位置top = 0top == 0top == MaxSize先赋值,再 ++toptop--,再取值

408 考研默认采用 top = -1 的约定,但做题务必看清题目条件。两种约定下入栈/出栈的操作顺序恰好相反

核心操作#

入栈(top=-1 约定):

bool Push(SqStack *S, int x) {
if (S->top == MaxSize - 1) // 栈满上溢
return false;
S->data[++S->top] = x; // 先移指针,再赋值
return true;
}

出栈(top=-1 约定):

bool Pop(SqStack *S, int *x) {
if (S->top == -1) // 栈空下溢
return false;
*x = S->data[S->top--]; // 先取值,再移指针
return true;
}

复杂度分析#

操作时间复杂度说明
入栈/出栈/取栈顶O(1)只在栈顶操作,无需移动元素
空间复杂度O(1)辅助空间常数级

与顺序表对比:顺序表的插入/删除需要 O(n) 时间移动元素,而栈由于只在一端操作,所有操作均为 O(1)。


共享栈#

核心思想#

用一个大小为 MaxSize 的数组同时存放两个栈,相向生长:

下标: 0 1 2 ... ... MaxSize-2 MaxSize-1
[s1 s1 s1] → 空闲区 ← [s2 s2 s2 ]
↑ top1 ↑ top2
  • 栈 1:栈底在下标 0top1 从左向右增长
  • 栈 2:栈底在下标 MaxSize-1top2 从右向左增长

状态判断(⭐高频)#

状态条件
栈 1 空top1 == -1
栈 2 空top2 == MaxSize
栈满top1 + 1 == top2

⚠️ 易错:栈满条件是两个栈顶指针相邻,不是某一个栈到达数组中点。一个栈可以占用超过一半的空间,只要另一个栈还有余量。

入栈/出栈#

bool Push(SharedStack *s, int stackNum, int x) {
if (s->top1 + 1 == s->top2) // 栈满
return false;
if (stackNum == 1)
s->data[++s->top1] = x; // 栈1:指针右移后入栈
else
s->data[--s->top2] = x; // 栈2:指针左移后入栈
return true;
}

优点#

  • 提高空间利用率
  • 适用于两个栈此消彼长的场景(如一个增长时另一个收缩)

链栈#

存储结构#

typedef struct LinkNode {
int data;
struct LinkNode *next;
} LinkNode, *LinkStack;

逻辑结构(以链表头部作为栈顶):

栈顶 → [a₃|next] → [a₂|next] → [a₁|NULL]

核心操作#

入栈(头插法):

bool Push(LinkStack &top, int x) {
LinkNode *s = (LinkNode *)malloc(sizeof(LinkNode));
if (s == NULL) return false;
s->data = x;
s->next = top;
top = s;
return true;
}

出栈(删头结点):

bool Pop(LinkStack &top, int &x) {
if (top == NULL) return false; // 栈空
LinkNode *p = top;
x = p->data;
top = p->next;
free(p);
return true;
}

易错:链栈通常不需要头结点,栈顶指针直接指向链表第一个结点。判空条件是 top == NULL

顺序栈 vs 链栈#

对比项顺序栈链栈
存储方式静态数组,连续空间链表,离散空间
栈满溢出可能溢出不会溢出
空间利用可能浪费(预分配过大)按需分配
存储密度高(无指针开销)低(每个结点多一个指针)
缓存性能好(连续存储)差(离散存储)

考研高频考点#

  • ⭐ 合法出栈序列的判断(选择题超高频)
  • top 初始值不同时入栈/出栈代码的区别
  • ⭐ 共享栈栈满条件 top1 + 1 == top2
  • ⭐ 链栈 vs 顺序栈的优缺点对比(简答题)
  • 卡特兰数计算合法出栈序列总数
  • 栈的三种实现(顺序栈、共享栈、链栈)的适用场景选择

关联页面#

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

栈(Stack)
https://lingluoa.icu/posts/stack/
作者
lingluoa
发布于
2026-07-05
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0