C++开发基础之栈的用法和应用场景介绍

在这里插入图片描述

1. 栈(stack)是什么?

在 C++ 标准库中,std::stack 是一个容器适配器(container adapter),它并不是独立的数据结构,而是基于其他顺序容器(如 dequevector)实现的。
特点:

  • 遵循 先进后出(FILO / LIFO) 原则。
  • 只能在栈顶进行插入和删除操作。
  • 常用于表达式求值、括号匹配、函数调用栈模拟等。

2. 头文件与命名空间

#include <stack>
#include <iostream>
using namespace std;

3. 常用 API

方法作用
push(value)向栈顶压入一个元素
pop()移除栈顶元素(不会返回值)
top()获取栈顶元素的引用
empty()判断栈是否为空
size()返回栈中元素个数
swap(other)交换两个栈的内容

⚠️ 注意:pop() 只是删除,不会返回元素值。如果想取值,应先用 top(),再 pop()

4. 基本用法示例

#include <stack>
#include <iostream>
using namespace std;

int main() {
    stack<int> s;   // 定义一个空栈,存放 int 类型

    // 入栈
    s.push(10);
    s.push(20);
    s.push(30);

    cout << "当前栈顶元素: " << s.top() << endl; // 输出 30

    // 出栈
    s.pop();
    cout << "出栈后栈顶: " << s.top() << endl; // 输出 20

    cout << "栈中元素数量: " << s.size() << endl;

    // 遍历栈(需循环 pop)
    cout << "栈中所有元素: ";
    while (!s.empty()) {
        cout << s.top() << " ";
        s.pop();
    }
    cout << endl;

    return 0;
}

运行结果:

在这里插入图片描述

5. 自定义类型

stack 可以存储结构体或类对象。

struct Point {
    int x, y;
};

int main() {
    stack<Point> st;
    st.push({1, 2});
    st.push({3, 4});

    cout << "Top Point: (" << st.top().x << ", " << st.top().y << ")" << endl;
    return 0;
}

在这里插入图片描述

6. 栈的应用场景

一句话来总结就是:最近的事先处理,过去的事后处理在这里插入图片描述

1. 函数调用栈(最近调用的函数先返回)

  • 场景:在 C++ 或其他语言里,每次调用函数时,系统会把当前执行环境压入栈(函数参数、局部变量、返回地址)。

  • 特点:最近调用的函数,一定是第一个完成并返回的。

  • 例子

    void A() { B(); }
    void B() { C(); }
    void C() { cout << "C Done\n"; }
    
    int main() {
        A();
        return 0;
    }
    

    执行顺序就是:main -> A -> B -> C,返回时是 C → B → A → main,完全符合 最近的事先处理。这里的 “函数调用 → 返回顺序” 背后依赖的就是 调用栈(Call Stack) 机制。

1. 每次调用函数 → 压栈(Push)

当一个函数被调用时,系统会把“当前的执行现场”压入栈里,通常包含:

  • 返回地址(调用完要跳回的地方)
  • 参数
  • 局部变量
  • 寄存器保存状态

这些组成了一个 栈帧(Stack Frame)

2. 函数执行完 → 出栈(Pop)

函数运行结束后,这个栈帧会被弹出(Pop),系统恢复到上一个函数的上下文。

3. 栈的特征体现

以上面的代码为例:

#include <iostream>

void C() { std::cout << "C Done\n"; }
void B() { C(); }
void A() { B(); }

int main() {
    A();
    return 0;
}

执行过程(调用时 压栈):

  1. main() 进栈
  2. 调用 A()A 的栈帧压栈
  3. 调用 B()B 的栈帧压栈
  4. 调用 C()C 的栈帧压栈

执行结束(返回时 出栈):

  • C 返回 → C 栈帧出栈
  • B 返回 → B 栈帧出栈
  • A 返回 → A 栈帧出栈
  • 最后回到 main

这就是 “最近调用的函数,最先返回”,完全符合 栈的 LIFO 原则

4. 实际中的“调用堆栈”

在调试器(如 Visual Studio、gdb)里,能直接看到这个调用栈:

  • 在 VS 里是 Call Stack 窗口
  • 在 gdb 里用 bt(backtrace)命令。

例如,当代码执行到 C 的时候,调用栈显示:

在这里插入图片描述
这就是函数调用的堆栈链路。

函数调用过程天然体现了 栈结构的使用,它不需要手动写 std::stack,而是由编译器和操作系统自动管理的“调用堆栈(Call Stack)”。

2. 表达式计算(最近遇到的操作符,先处理)

  • 场景:中缀表达式 3 + (2 * 5) 转换/计算时,括号里的操作需要先完成。

  • 特点:括号表达式里,最后遇到的左括号 (,先被处理并匹配

  • 例子

    • 输入:(1+(2*3))
    • 栈执行过程:( 入栈 → 2*3 先计算 → 再算 1+结果
  • 实际应用:计算器程序、编译器语法解析。

表达式求值(逆波兰表达式 RPN)

int evalRPN(vector<string>& tokens) {
    stack<int> s;
    for (auto& t : tokens) {
        if (t == "+" || t == "-" || t == "*" || t == "/") {
            int b = s.top(); s.pop();
            int a = s.top(); s.pop();
            if (t == "+") s.push(a+b);
            if (t == "-") s.push(a-b);
            if (t == "*") s.push(a*b);
            if (t == "/") s.push(a/b);
        } else {
            s.push(stoi(t));
        }
    }
    return s.top();
}

3. 回溯算法(最近的一步先撤销)

  • 场景:在迷宫寻路中,如果走到死胡同,要回退到最近的一步重新尝试。

  • 特点:最近走的一步,最先被撤销。

  • 例子

    • 走迷宫路径 A → B → C → D,发现 D 不通 → 弹出 D → 回到 C。
  • 实际应用:数独求解、八皇后问题、DFS 搜索。

4. 撤销 / 恢复操作(最近的操作先撤销)

  • 场景:文本编辑器(如 Word、VS Code)里,撤销(Undo)功能常用栈实现。

  • 特点:最后输入的字符,最先被撤销。

  • 例子

    • 操作顺序:输入 ABC
    • 栈状态:A → B → C
    • 撤销:先撤销 C → 再撤销 B → 再撤销 A
  • 实际应用:编辑器、IDE、绘图软件。

5. 浏览器前进/后退(最近访问的页面先返回)

  • 场景:浏览器的历史记录。

  • 特点

    • 后退:最近访问的页面先返回。
    • 前进:另一个栈保存“未来”页面,形成 双栈模型
  • 例子

    • 访问顺序:A → B → C
    • 后退:栈顶 C 弹出 → 回到 B
  • 实际应用:浏览器、APP 页面导航。

6. 数据反转(最近存进去的数据,先取出来)

  • 场景:需要把一个序列翻转过来。

  • 特点:栈的 LIFO 特性天然可以完成反转。

  • 例子

    • 数制转换:10 进制转 2 进制时,每次 %2 的余数压栈,最后从栈中依次弹出就得到正确顺序。
    • 字符串反转:把 hello 压栈 → 依次弹出得到 olleh
  • 实际应用:字符串处理、逆序输出。

数制转换(十进制 → 二进制)

void toBinary(int n) {
    stack<int> s;
    while (n > 0) {
        s.push(n % 2);
        n /= 2;
    }
    while (!s.empty()) {
        cout << s.top();
        s.pop();
    }
    cout << endl;
}

7. 小结

“最近的事先处理,过去的事后处理” 在实际场景中的体现:

场景为什么用栈
函数调用最近调用的函数必须先返回
表达式求值最近的括号先匹配,最近的运算符先执行
回溯算法最近的一步必须先撤销
撤销操作最近的编辑必须先撤销
浏览器导航最近访问的页面先返回
数据反转最近压入的数据先取出,正好实现逆序

7. 总结

  • 栈是一种只允许在一端操作的数据结构,遵循 LIFO(后进先出) 原则。
  • 在 C++ 中可通过 std::stack 使用。
  • 典型应用:函数调用栈、表达式求值、回溯算法、撤销恢复、浏览器历史、数据反转。
  • 关键记忆点:最近的事先处理,过去的事后处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dotnet研习社

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值