C++ STL 栈(stack)与队列(queue)容器详解

C++ STL 栈(stack)与队列(queue)容器详解

📚 思维导图概览

一、栈(stack)容器

1.1 基本概念

栈(Stack)是一种先进后出(FILO: First In Last Out)的线性数据结构,操作特性类似于现实中的子弹夹或叠盘子。其核心操作限制为:

  • 只能在容器的一端(栈顶)进行插入和删除操作
  • 不提供遍历功能,只能访问栈顶元素
  • 典型应用场景:函数调用栈、表达式求值、括号匹配等

示意图:

1.2 底层实现探秘

stack作为容器适配器,默认使用deque作为底层容器,但我们也可以自由选择:

底层容器特性对比适用场景
deque默认选择,头尾操作高效通用场景
vector动态数组,尾部操作O(1)需要连续内存空间时
list双向链表,任意位置操作O(1)频繁插入删除中间元素时

底层代码如下:

// 自定义底层容器的实现方法
stack<int, vector<int>> vecStack;   // 使用vector作为底层
stack<string, list<string>> listStack; // 使用list作为底层

二、接口全解与性能分析

2.1 核心接口性能表

操作时间复杂度注意事项
push()O(1)可能触发容器扩容
pop()O(1)空栈调用导致未定义行为
top()O(1)返回引用,可直接修改栈顶元素
empty()O(1)建议先检查非空再操作
size()O(1)返回值为无符号类型

2.2 相关接口关键字如下:

构造函数:

  • stack<T> stk;                        //stack采用模板类实现, stack对象的默认构造形式
  • stack(const stack &stk);       //拷贝构造函数

赋值操作:

  • stack& operator=(const stack &stk);          //重载等号操作符

数据存取:

  • push(elem);              //向栈顶添加元素
  • pop();                       //从栈顶移除第一个元素
  • top();                        //返回栈顶元素

大小操作:

  • empty();           //判断堆栈是否为空
  • size();               //返回栈的大小

2.3 相关接口案例代码如下:

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


void test01()
{
    // 特点:先进后出数据结构
    stack<int>s;
    
    // 入栈
    s.push(10);
    s.push(20);
    s.push(30);
    s.push(40);

    cout<<"栈的大小为:"<<s.size()<<endl;

    // 只要栈不为空,查看栈顶,并且执行出栈操作
    while (!s.empty())
    {
        // 查看栈顶元素
        cout<<"栈顶元素为:"<<s.top()<<endl;

        // 出栈
        s.pop();
    }

    cout<<"栈的大小为:"<<s.size()<<endl;
    
}

int main()
{
    test01();
    system("pause");
    return 0;
}

2.4 经典错误案例

stack<int> s;
s.pop();   // 错误!空栈弹出导致崩溃
s.top() = 5; // 正确,可以直接修改栈顶值

// 安全操作示范
if (!s.empty()) {
    int val = s.top();
    s.pop();
}

1.4 应用场景

  • 撤销操作(Undo)机制
  • 递归函数调用
  • 浏览器的前进后退功能
  • 迷宫问题路径记录

二、队列(queue)容器

2.1 基本概念

队列(Queue)是一种先进先出(FIFO: First In First Out)的线性数据结构,操作特性类似于现实中的排队场景:

  • 允许在队尾添加元素(入队)
  • 允许在队头移除元素(出队)
  • 典型应用场景:消息队列、打印机任务队列、BFS算法

示意图:

2.2 常用接口

构造函数

queue<T> que;               // 默认构造
queue<T, list<T>> que;      // 指定list为底层容器
queue(const queue& que);    // 拷贝构造

数据操作

方法功能描述时间复杂度
push(elem)元素入队O(1)
pop()队头元素出队O(1)
front()返回队头元素O(1)
back()返回队尾元素O(1)

容量操作

方法功能描述时间复杂度
empty()判断队列是否为空O(1)
size()返回队列元素个数O(1)

2.3相关接口案例代码如下:

#include<iostream>
using namespace std;
#include<queue>
#include<string>

class Person
{
public:
    Person(string name, int age)
    {
        this->m_Name = name;
        this->m_Age = age;
    }
    string m_Name;
    int m_Age;
};

void test01()
{
    // 特点:先进先出数据结构
    // 创建队列
    queue<Person>q;

    // 准备数据
    Person p1("迪迦", 100);
    Person p2("泰罗", 150);
    Person p3("戴拿", 50);
    Person p4("雷欧", 200);

    // 入队
    q.push(p1);
    q.push(p2);
    q.push(p3);
    q.push(p4);

    cout<<"队列大小为:"<<q.size()<<endl;

    // 判断只要队列不为空,查看队头和队尾,出队
    while (!q.empty())
    {
        // 查看队头
        cout<<"队头元素————姓名:"<<q.front().m_Name<<"年龄:"<<q.front().m_Age<<endl;

        // 查看队尾
        cout<<"队尾元素————姓名:"<<q.back().m_Name<<"年龄:"<<q.back().m_Age<<endl;

        // 出队
        q.pop();
    }

    cout<<"队列大小为:"<<q.size()<<endl;
    

}

int main()
{
    test01();
    system("pause");
    return 0;
}

2.4 应用场景

  • 多线程任务调度
  • 网络数据包缓冲
  • 广度优先搜索(BFS)
  • 订单处理系统

三、对比与选择

特性stackqueue
数据结构FILO(先进后出)FIFO(先进先出)
操作端单端操作(栈顶)双端操作(队头/队尾)
典型应用撤销机制、递归处理任务调度、消息缓冲
底层实现默认deque,可配vector/list默认deque,可配list
遍历方式需要转移元素需要转移元素

四、注意事项

  1. 容器适配器:stack/queue是基于其他容器实现的适配器
  2. 元素访问限制
    • stack只能访问top元素
    • queue只能访问front和back元素
  3. 线程安全:STL容器非线程安全,需自行加锁
  4. 异常处理:empty检查后再进行top/front/pop操作

五、常见面试题

Q1:用两个栈实现队列?

思路:使用两个栈,一个负责入队(in),一个负责出队(out)。当需要出队时,若 out 栈为空,则将 in 栈中的元素全部倒入 out 栈。

#include <stack>
using namespace std;

class MyQueue {
private:
    stack<int> in;
    stack<int> out;

    void transfer() {
        while (!in.empty()) {
            out.push(in.top());
            in.pop();
        }
    }

public:
    MyQueue() {}
    
    void push(int x) {
        in.push(x);
    }
    
    int pop() {
        if (out.empty()) transfer();
        int val = out.top();
        out.pop();
        return val;
    }
    
    int peek() {
        if (out.empty()) transfer();
        return out.top();
    }
    
    bool empty() {
        return in.empty() && out.empty();
    }
};

Q2:用队列实现栈?

思路:使用单个队列,每次插入新元素后,将前面的元素依次出队再入队,使得新元素位于队首。

#include <queue>
using namespace std;

class MyStack {
private:
    queue<int> q;

public:
    MyStack() {}
    
    void push(int x) {
        q.push(x);
        for (int i = 0; i < q.size() - 1; ++i) {
            q.push(q.front());
            q.pop();
        }
    }
    
    int pop() {
        int val = q.front();
        q.pop();
        return val;
    }
    
    int top() {
        return q.front();
    }
    
    bool empty() {
        return q.empty();
    }
};

Q3:最小栈设计(O(1)时间获取最小值)?

思路:使用辅助栈同步记录当前最小值。

#include <stack>
using namespace std;

class MinStack {
private:
    stack<int> s;
    stack<int> min_s;

public:
    MinStack() {}
    
    void push(int val) {
        s.push(val);
        if (min_s.empty() || val <= min_s.top()) {
            min_s.push(val);
        }
    }
    
    void pop() {
        if (s.top() == min_s.top()) {
            min_s.pop();
        }
        s.pop();
    }
    
    int top() {
        return s.top();
    }
    
    int getMin() {
        return min_s.top();
    }
};

Q4:循环队列实现?

思路:使用数组存储数据,维护头尾指针 front 和 rear,并通过 size 变量简化边界判断。

class MyCircularQueue {
private:
    vector<int> data;
    int front;
    int rear;
    int size;
    int capacity;

public:
    MyCircularQueue(int k) {
        data.resize(k);
        front = 0;
        rear = 0;
        size = 0;
        capacity = k;
    }
    
    bool enQueue(int value) {
        if (isFull()) return false;
        data[rear] = value;
        rear = (rear + 1) % capacity;
        size++;
        return true;
    }
    
    bool deQueue() {
        if (isEmpty()) return false;
        front = (front + 1) % capacity;
        size--;
        return true;
    }
    
    int Front() {
        return isEmpty() ? -1 : data[front];
    }
    
    int Rear() {
        return isEmpty() ? -1 : data[(rear - 1 + capacity) % capacity];
    }
    
    bool isEmpty() {
        return size == 0;
    }
    
    bool isFull() {
        return size == capacity;
    }
};

Q5:栈的压入、弹出序列合法性判断?

思路:模拟压栈过程,遍历弹出序列,若当前栈顶不是目标元素,则按压入序列继续压栈。

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

bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
    stack<int> s;
    int i = 0;
    for (int num : pushed) {
        s.push(num);
        while (!s.empty() && s.top() == popped[i]) {
            s.pop();
            i++;
        }
    }
    return s.empty();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小程同学>o<

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

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

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

打赏作者

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

抵扣说明:

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

余额充值