Skip to content

链栈

核心思想

链栈的核心特点:

  • 以链表头部作为栈顶:入栈和出栈都在链表头部进行,时间复杂度 O(1)
  • 无需预分配空间:每个结点动态分配,不会栈满溢出(除非内存耗尽)
  • 不支持随机访问:访问栈中第 i 个元素需要从栈顶依次遍历

结点结构定义:

c
typedef struct LinkNode {
    int data;             // 数据域
    struct LinkNode *next; // 指针域,指向下一个结点
} LinkNode, *LinkStack;

链栈的逻辑结构:

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

栈顶指针 top 始终指向链表的第一个结点(即栈顶元素)。

交互可视化

通过下方的交互动画,你可以逐步观察链栈的执行过程:

加载可视化中...

操作详解

入栈操作

入栈即在链表头部插入(头插法)一个新结点。

关键步骤:

  1. 创建新结点,赋值数据域
  2. 新结点的 next 指向当前栈顶
  3. 更新栈顶指针指向新结点
c
// 入栈(不带头结点)
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;
}

链栈入栈不需要判断栈满,只要内存足够就能继续入栈。

出栈操作

出栈即删除链表头结点,并取出其数据。

关键步骤:

  1. 判断栈是否为空(top == NULL
  2. 取出栈顶元素的值
  3. 栈顶指针后移一位
  4. 释放原栈顶结点
c
// 出栈(不带头结点)
bool Pop(LinkStack &top, int &x) {
    if (top == NULL) return false; // 栈空
    LinkNode *p = top;
    x = p->data;
    top = p->next;
    free(p);
    return true;
}

复杂度分析

操作时间复杂度说明
入栈O(1)头插法,无需移动元素
出栈O(1)删除头结点,无需移动元素
读取栈顶O(1)直接访问 top 指向的结点
判空O(1)判断 top 是否为 NULL

空间复杂度:每个元素需要额外存储一个指针域,存储密度低于顺序栈。

链栈 vs 顺序栈对比

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

易错:链栈通常不需要头结点——栈顶指针直接指向链表第一个结点(即栈顶元素)。这和带头结点的单链表不同,判空条件是 top == NULL 而不是 top->next == NULL

考研高频考点

  • ⭐ 链栈的入栈和出栈操作实现(头插法/删头结点)
  • ⭐ 链栈 vs 顺序栈的优缺点对比(简答题高频)
  • ⭐ 链栈不存在栈满溢出问题的原因(概念题)
  • 带头结点和不带头结点的链栈实现差异
  • 共享栈、顺序栈、链栈的适用场景选择

相关知识

  • 顺序栈是链栈的对照:顺序栈随机访问快、缓存友好,但容量固定;链栈动态扩展、无上溢,但每个结点多一个指针开销
  • 链栈的入栈/出栈本质上就是单链表的头插法和删头结点操作
  • 表达式求值是栈的经典应用,无论用顺序栈还是链栈实现,算法逻辑完全相同