标题里的“四种形态”真不是难理解,但它最容易让人懵在一点:这四个词根本不是一个体系里平级的概念,而是两两组合的结果。
很多人学栈时已经能很溜地写括号匹配、后缀表达式求值,但只要看到“递增栈”、“递减栈”、“满栈”、“空栈”这种组合词,一下就会卡壳。尤其是去网上搜资料,你会发现嵌入式方向的文章说这是 ARM 栈的四种寻址方式,算法方向的文章又把它解释成“单调递增栈”、“单调递减栈”,两边还都对,初学者直接被绕晕。
这篇内容就专门把这件事讲透:先用两组维度拆出“四种形态”的来历,再说清楚算法里的“单调递增/递减栈”到底在维护什么,最后用内存布局、伪代码和真实硬件栈把这几个概念串成一张能直接记住的图。读完你至少不会再害怕“栈有四种形态”这个面试或考试高频点。
1. 先拆开:标题里其实藏着两组不同维度
1.1 “递增/递减”和“满/空”不是并列关系
经常看到有人写“栈有四种形态:递增栈、递减栈、满栈、空栈”,把四个词摆成并列的一排。这个说法严格来讲不严谨,它真正想表达的是:
- 维度一:栈的“增长方向”,分为递增(向高地址方向增长)和递减(向低地址方向增长)。
- 维度二:栈指针(SP / top)的“指向含义”,分为满栈(指向栈顶最后一个数据)和空栈(指向下一个可用的空闲位置)。
把这两个维度交叉,才会得到四组组合:
| 组合 | 地址增长方向 | 栈指针指向 | 常见叫法 |
|---|---|---|---|
| FA | 向高地址 | 栈中最后的元素 | 满递增栈 |
| FD | 向低地址 | 栈中最后的元素 | 满递减栈 |
| EA | 向高地址 | 下一个空闲位置 | 空递增栈 |
| ED | 向低地址 | 下一个空闲位置 | 空递减栈 |
以“满递减”为例,它的专业含义是:每一次压栈都让地址往更小的方向移动,而且栈指针始终停留在刚刚写入的那个数据单元上。这正好是 CPU 里最常出现的模型,x86、ARM Cortex-M 的默认栈基本都长这样。
所以以后看到“四种形态”,脑子里第一反应应该是“这是二维表格”,不是一条线性列表。谁在跟谁组合,才是理解整件事的钥匙。
1.2 “递增/递减”在算法题中是另一码事
标题里还有一层容易混淆:算法领域也经常说“单调递增栈”、“单调递减栈”。这里的“递增/递减”说的是“栈内元素从栈底到栈顶的大小关系”,和内存地址往哪个方向长没有直接关系。
举个例子,压入元素序列 [2, 1, 4, 3],如果你维护的是一个“从栈底到栈顶严格递增”的栈,压完 1 的时候栈内元素大概长这样:
text复制栈底 -> 栈顶
1
如果维护的是“从栈底到栈顶严格递减”的栈,压完 2 和 1 时要先把 2 弹出,栈内只剩 1:
text复制栈底 -> 栈顶
1
可以看到,同样一个输入序列,不同的“单调规则”会触发不同的弹栈动作,从而解决完全不同的问题。这就是算法语境下的“递增/递减”,它关心的是元素值的大小顺序,不关心内存地址。
“满栈/空栈”这个词在算法语境下反而少见,算法里通常说“栈满”、“栈空”,表示容量或状态,比如顺序栈 top == maxSize 时不能继续 push,这是另一个层面的概念。为了避免一开始就被这些同音词带偏,必须把两套体系分清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从数组模拟看递增栈和递减栈的实现差异
2.1 数组下标增长天然就是“地址递增”
学数据结构时,大多数人对栈的第一次建模来自数组。顺序栈代码大概是这么写的:
c复制#define MAXSIZE 100
typedef struct {
int data[MAXSIZE];
int top;
} SqStack;
当 top 从 -1 开始,每压入一个元素就执行 data[++top] = x,栈底在 data[0],栈顶随着数组下标往大方向走。这就是最淳朴的“递增栈”模型:地址(下标)越来越大,最后一个数据所在的位置是 data[top]
