Skip to content

栈与栈的应用 | 知乎@柠檬沙棘1996

定义

栈(stack是限定仅在表尾进行插入和删除操作的线性表。栈遵循后进先出(Last In First Out,LIFO)的特点,即最后插入的元素最先被删除。

允许插入和删除的一端称为栈顶(top,另一端则称为栈底(bottom:

栈的特殊之处在于其限制了元素插入与删除操作的可执行位置。栈的插入操作称为入栈(push),也可称为压栈、进栈等;删除操作称为出栈(pop),也可称为弹栈。将栈比作一个弹夹,同时将栈内元素比作子弹来理解栈的概念会形象得多。

Note

注意栈首先是一个线性表,也就是说,栈的元素具有线性关系,即前后驱动关系。

栈的顺序存储结构

栈的本质是一个线性表,那么基于线性表的两种基本存储逻辑,也就可以通过顺序存储结构链式存储结构两种方式实现。

顺序栈的实现

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

基本操作

初始化

void InitStack(SqStack *S) {
    S->top = -1;
}

判空

bool StackEmpty(SqStack S) {
    return S.top == -1;
}

入栈

bool Push(SqStack *S, ElemType e) {
    if (S->top == MaxSize - 1) { // 栈满
        return false;
    }
    S->data[++S->top] = e; // 栈顶指针加1,将元素e入栈
    return true;
}

出栈

bool Pop(SqStack *S, ElemType *e) {
    if (S->top == -1) { // 栈空
        return false;
    }
    *e = S->data[S->top--]; // 出栈,栈顶指针减1
    return true;
}

获取栈顶元素

bool GetTop(SqStack S, ElemType *e) {
    if (S.top == -1) { // 栈空
        return false;
    }
    *e = S.data[S.top]; // 获取栈顶元素
    return true;
}

共享栈

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一维数组空间,将两个栈的栈底分别设置在数组的两端,两个栈顶向数组中间延伸,这就构成了一组共享栈

栈的链式存储结构

栈也可基于链表实现,称为链栈

栈元素的删除与插入操作均在栈顶进行,因此链表的头部通常可以直接作为栈顶。

对于链栈而言,基本不存在栈满的情况,因为链表可以动态扩展。

链栈的实现

typedef struct StackNode {
    ElemType data;
    struct StackNode *next;
} StackNode, *LinkStack;

基本操作与链表类似,注意链表是否具有头节点。

栈的典型应用

栈与队列的应用

栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考方位缩小,更加聚焦于我们所要解决的问题核心。

以下是栈的几个典型应用场景:

  1. 表达式求值:栈可以用于中缀表达式转后缀表达式(逆波兰表示法)以及后缀表达式的求值。

  2. 函数调用管理:程序的函数调用过程可以看作是一个栈结构,函数的参数、局部变量等信息都保存在栈帧中。

  3. 撤销操作:许多应用程序(如文本编辑器)都使用栈来实现撤销操作,用户的每一步操作都会被压入栈中,撤销时则弹出栈顶操作。

  4. 深度优先搜索:在图的遍历中,深度优先搜索(DFS)可以使用栈来实现,记录当前路径并回溯。