C++11(stack和queue)

📚 吃透 C++ STL 栈和队列
1 🥞 栈(stack):先进后出的 "叠盘子"
2 🚶 队列(queue):先进先出的 "排队买奶茶"
3 🏆 优先队列(priority_queue)
4 🧩 容器适配器:"套壳" 的底层逻辑
5 📝 新手避坑 & 复习总结

1 🥞 栈(stack):先进后出的 "叠盘子"
1.1 新手理解:啥是栈?

想象你往桌子上叠盘子:先放的盘子在最下面,后放的在最上面;拿的时候只能先拿最上面的,这就是后进先出(LIFO) ------ 栈的核心逻辑。

C++ 里的 stack 不是 "原生容器",而是 "容器适配器"(后面会讲),默认用 deque 当底层存储,新手不用纠结底层,先会用接口就行!

1.2 必背接口(记这 6 个就够)

💡 避坑:

  • 别直接用top()!如果栈空,调用top()会直接崩溃,一定要先判断!s.empty();
  • pop()只删元素,不返回值!想拿栈顶元素要先top()再pop()。

1.3 代码示例

cpp 复制代码
#include <iostream>
#include <stack> // 必须包含头文件!
using namespace std;

int main() {
    stack<int> s;
    // 压栈(叠盘子)
    s.push(1);
    s.push(2);
    s.push(3);
    
    cout << "栈的大小:" << s.size() << endl; // 输出3
    cout << "栈顶元素:" << s.top() << endl; // 输出3
    
    // 出栈(拿盘子)
    s.pop();
    cout << "出栈后栈顶:" << s.top() << endl; // 输出2
    
    // 遍历栈(新手注意:栈没有迭代器,只能边删边看)
    while (!s.empty()) {
        cout << s.top() << " "; // 输出2 1
        s.pop();
    }
    return 0;
}

1.4 经典刷题场景(新手版解析)
(1)最小栈(面试高频)

**问题:**实现一个栈,能在 O (1) 时间拿到最小值(新手理解:不管栈里有多少元素,找最小值都超快)。

**思路:**用两个栈,一个存所有元素(主栈),一个存 "当前最小值"(辅助栈)。

cpp 复制代码
#include <iostream>
#include <stack>
using namespace std;

class MinStack {
public:
    // 压栈:新元素≤辅助栈顶,才放进辅助栈
    void push(int x) {
        _data.push(x);
        // 新手注意:辅助栈空 或 新元素更小,才入栈
        if (_min.empty() || x <= _min.top()) {
            _min.push(x);
        }
    }

    // 出栈:主栈顶=辅助栈顶,辅助栈才出栈
    void pop() {
        if (!_data.empty()) {
            if (_data.top() == _min.top()) {
                _min.pop();
            }
            _data.pop();
        }
    }

    // 取栈顶
    int top() {
        return _data.top();
    }

    // 取最小值(直接拿辅助栈顶)
    int getMin() {
        return _min.top();
    }

private:
    stack<int> _data; // 存所有元素
    stack<int> _min;  // 存最小值
};

// 测试(新手可以直接运行)
int main() {
    MinStack ms;
    ms.push(3);
    ms.push(2);
    ms.push(5);
    cout << "最小值:" << ms.getMin() << endl; // 输出2
    ms.pop();
    ms.pop();
    cout << "最小值:" << ms.getMin() << endl; // 输出3
    return 0;
}

(2)逆波兰表达式求值

新手理解:逆波兰表达式就是把运算符放数字后面,比如 3 4 + 就是 3+4,不用括号,超适合用栈算。

cpp 复制代码
#include <iostream>
#include <stack>
#include <vector>
#include <string>
using namespace std;

int evalRPN(vector<string>& tokens) {
    stack<int> s;
    for (string& str : tokens) {
        // 是数字:转成int入栈
        if (str != "+" && str != "-" && str != "*" && str != "/") {
            s.push(stoi(str)); // stoi:字符串转int(新手记住这个函数)
        } else {
            // 是运算符:出栈两个数计算
            int right = s.top(); s.pop(); // 右操作数(后出栈的是右边)
            int left = s.top(); s.pop();  // 左操作数
            if (str == "+") s.push(left + right);
            if (str == "-") s.push(left - right);
            if (str == "*") s.push(left * right);
            if (str == "/") s.push(left / right); // 题目保证除数≠0
        }
    }
    return s.top(); // 最后栈里只剩结果
}

// 测试(新手可运行)
int main() {
    // 计算 (1+2)*3 → 逆波兰表达式:1 2 + 3 *
    vector<string> tokens = {"1","2","+","3","*"};
    cout << "结果:" << evalRPN(tokens) << endl; // 输出9
    return 0;
}

1.5 模拟实现(不用纠结底层)

新手不用写标准库的复杂版本,先实现一个基于 vector 的简单栈,理解核心逻辑:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

// 新手版:自己写栈
template<class T>
class MyStack {
public:
    // 压栈:尾插
    void push(const T& x) {
        _vec.push_back(x);
    }

    // 出栈:尾删
    void pop() {
        if (!empty()) {
            _vec.pop_back();
        }
    }

    // 取栈顶
    T& top() {
        return _vec.back();
    }

    // 判空
    bool empty() {
        return _vec.empty();
    }

    // 大小
    int size() {
        return _vec.size();
    }

private:
    vector<T> _vec; // 用vector存元素(新手最熟悉的容器)
};

// 测试
int main() {
    MyStack<int> s;
    s.push(10);
    s.push(20);
    cout << s.top() << endl; // 20
    s.pop();
    cout << s.size() << endl; // 1
    return 0;
}

2 🚶 队列(queue):先进先出的 "排队买奶茶"

2.1 新手理解:啥是队列?

排队买奶茶,先到的人先买走,后到的人排最后 ------ 这就是先进先出(FIFO),队列的核心逻辑。

C++ 的 queue 也是容器适配器,默认用 deque 当底层,新手重点记 "队尾进、队头出"。

2.2 必背接口(记 7 个)

💡 避坑:

  • front()和back()也要先判断!q.empty(),否则崩溃;
  • pop()只删队头,不返回值!

2.3 新手能看懂的代码示例

cpp 复制代码
#include <iostream>
#include <queue> // 必须包含头文件
using namespace std;

int main() {
    queue<int> q;
    // 入队(排队)
    q.push(1);
    q.push(2);
    q.push(3);
    
    cout << "队头:" << q.front() << endl; // 1
    cout << "队尾:" << q.back() << endl;  // 3
    cout << "队列大小:" << q.size() << endl; // 3
    
    // 出队(买完走了)
    q.pop();
    cout << "出队后队头:" << q.front() << endl; // 2
    
    // 遍历队列(新手:边删边看)
    while (!q.empty()) {
        cout << q.front() << " "; // 2 3
        q.pop();
    }
    return 0;
}

2.4 刷题:用两个队列实现栈

**思路:**两个队列 "倒腾",每次只留最后一个元素当栈顶。

cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;

class MyStack {
public:
    // 压栈:直接放进q1
    void push(int x) {
        q1.push(x);
    }

    // 出栈:把q1的元素倒到q2,只剩最后一个
    int pop() {
        // 把q1除了最后一个,都倒到q2
        while (q1.size() > 1) {
            q2.push(q1.front());
            q1.pop();
        }
        // 取q1最后一个(栈顶)
        int res = q1.front();
        q1.pop();
        // 交换q1和q2,下次继续用q1
        swap(q1, q2);
        return res;
    }

    // 取栈顶:直接取q1队尾
    int top() {
        return q1.back();
    }

    // 判空
    bool empty() {
        return q1.empty() && q2.empty();
    }

private:
    queue<int> q1;
    queue<int> q2;
};

// 测试
int main() {
    MyStack ms;
    ms.push(1);
    ms.push(2);
    cout << "栈顶:" << ms.top() << endl; // 2
    cout << "出栈:" << ms.pop() << endl; // 2
    cout << "栈顶:" << ms.top() << endl; // 1
    return 0;
}

2.5 模拟实现

用 list 实现(list 头删效率高,适合队列):

cpp 复制代码
#include <iostream>
#include <list>
using namespace std;

template<class T>
class MyQueue {
public:
    // 入队:尾插
    void push(const T& x) {
        _list.push_back(x);
    }

    // 出队:头删
    void pop() {
        if (!empty()) {
            _list.pop_front();
        }
    }

    // 队头
    T& front() {
        return _list.front();
    }

    // 队尾
    T& back() {
        return _list.back();
    }

    bool empty() {
        return _list.empty();
    }

    int size() {
        return _list.size();
    }

private:
    list<T> _list; // list头删不用搬移元素,比vector快
};

3 🏆 优先队列(priority_queue):自带 "VIP" 的队列

3.1 新手理解:啥是优先队列?

排队买奶茶,VIP 用户不用排最后,直接插前面 ------ 优先队列就是 "谁优先级高,谁先出队",底层是 "堆 "(新手可以理解成 "自动排序的完全二叉树 ")。
默认是大堆(最大值优先),也能改成小堆(最小值优先)。

3.2 接口(记 5 个)

💡 避坑:

  • 优先队列没有size()?不,有!只是新手用得少;
  • top()是取堆顶,不是队头!优先队列没有front()/back()。

3.3 必学:大堆 vs 小堆

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
#include <functional> // 用greater必须包含这个头文件!
using namespace std;

int main() {
    vector<int> v = {3,1,4,2,5};
    
    // 1. 大堆(默认):最大值优先
    priority_queue<int> pq1(v.begin(), v.end());
    cout << "大堆顶:" << pq1.top() << endl; // 5
    
    // 2. 小堆:最小值优先(新手记格式!)
    // 格式:priority_queue<类型, 底层容器, greater<类型>>
    priority_queue<int, vector<int>, greater<int>> pq2(v.begin(), v.end());
    cout << "小堆顶:" << pq2.top() << endl; // 1
    
    return 0;
}

3.4 新手刷题:找数组中第 K 大的元素

思路:用小堆更高效(新手先学大堆版,简单):

cpp 复制代码
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

// 找第K大的元素(新手版:大堆)
int findKthLargest(vector<int>& nums, int k) {
    // 1. 把所有元素放进大堆
    priority_queue<int> pq(nums.begin(), nums.end());
    // 2. 删k-1个最大值,剩下的堆顶就是第K大
    for (int i = 0; i < k-1; i++) {
        pq.pop();
    }
    return pq.top();
}

// 测试
int main() {
    vector<int> nums = {3,2,1,5,6,4};
    int k = 2;
    cout << "第" << k << "大的元素:" << findKthLargest(nums, k) << endl; // 5
    return 0;
}

3.5 新手进阶:存储自定义类型(比如日期)

新手先理解:自定义类型要告诉编译器 "怎么比大小",也就是重载<或>运算符。

cpp 复制代码
#include <iostream>
#include <queue>
using namespace std;

// 日期类(新手简化版)
class Date {
public:
    Date(int y, int m, int d) : _year(y), _month(m), _day(d) {}

    // 重载<:用于大堆(比较日期大小)
    bool operator<(const Date& d) const {
        // 年大的大 → 月大的大 → 天大的大
        if (_year != d._year) return _year < d._year;
        if (_month != d._month) return _month < d._month;
        return _day < d._day;
    }

    // 重载<<:方便输出
    friend ostream& operator<<(ostream& out, const Date& d) {
        out << d._year << "-" << d._month << "-" << d._day;
        return out;
    }

private:
    int _year, _month, _day;
};

int main() {
    priority_queue<Date> pq;
    pq.push(Date(2024, 3, 10));
    pq.push(Date(2024, 3, 9));
    pq.push(Date(2024, 3, 11));
    
    cout << "优先级最高的日期:" << pq.top() << endl; // 2024-3-11
    return 0;
}

4 🧩 容器适配器:"套壳" 的底层逻辑

4.1 新手理解:啥是适配器?

适配器就是 "转换器"------ 比如你有个欧标插头,在中国用不了,买个适配器就能插国标插座。
STL 里的 stack/queue/priority_queue 就是 "容器适配器":它们不自己存数据,而是 "套" 在其他容器(比如 deque/vector/list)外面,只暴露自己需要的接口。

4.2 为啥默认用 deque?(新手大白话)

先简单记 deque 的特点:

  • 👍 优点:头尾加 / 删元素快(比 vector 头删快,比 list 省空间);
  • 👎 缺点:遍历慢(但 stack/queue 不用遍历,完美避开)。

新手不用纠结 deque 的底层结构,记住:

  • stack 只需要 "尾插 / 尾删",deque 比 vector 扩容快;
  • queue 需要 "尾插 / 头删",deque 比 list 省空间;
    所以 STL 选 deque 当默认底层容器。

4.3 新手能改的底层容器

cpp 复制代码
#include <iostream>
#include <stack>
#include <vector> // 用vector当stack的底层
#include <queue>
#include <list>   // 用list当queue的底层
using namespace std;

int main() {
    // 1. stack用vector当底层
    stack<int, vector<int>> s;
    s.push(1);
    s.push(2);
    cout << s.top() << endl; // 2
    
    // 2. queue用list当底层
    queue<int, list<int>> q;
    q.push(1);
    q.push(2);
    cout << q.front() << endl; // 1
    
    return 0;
}

5 📝 避坑 & 复习总结

5.1 常见错误(避坑指南)

1.调用top()/front()/back()前没判空 → 程序崩溃;

2.以为pop()会返回元素 → 其实pop()只删除,要先top()再pop();

3.用小堆时忘加#include → 编译报错;

4.优先队列用front()/back() → 优先队列只有top();

5.自定义类型放进优先队列,忘重载</> → 编译报错。

5.2 核心考点(复习重点)

1.stack:后进先出,核心接口push/pop/top,经典题:最小栈、逆波兰表达式;
2
.queue
:先进先出,核心接口push/pop/front/back,经典题:用队列模拟栈;

3priority_queue :堆结构,默认大堆,小堆要加greater,经典题:TopK 问题;

4.容器适配器:stack/queue 不是原生容器,默认底层是 deque,可自定义底层容器。

相关推荐
最后一支迷迭香1 小时前
苹果的MacOS系统适合做Java开发吗
java·开发语言·macos
m0_739030001 小时前
[特殊字符] Java 高频面试题汇总
java·开发语言·面试
2zcode1 小时前
基于MATLAB的5G物理层文本传输系统仿真与性能分析
开发语言·5g·matlab
用户805533698031 小时前
现代Qt开发教程(新手篇)2.1——QPainter 绘图基础
c++·qt
feifeigo1231 小时前
基于布谷鸟算法的配电网分布式电源选址定容 MATLAB 实现
开发语言·算法·matlab
Mike117.1 小时前
GBase 8a 宽表查询里的压缩和行存列取舍
java·开发语言·数据库
辰尘_星启2 小时前
【ROS2】 Python 节点的开发流程
开发语言·python·机器人·系统·控制·ros2
计算机安禾2 小时前
【c++面向对象编程】第12篇:继承(二):构造与析构顺序,继承中的构造函数
开发语言·c++
知识分享小能手2 小时前
R语言入门学习教程,从入门到精通,R语言获取数据 (7)
开发语言·学习·r语言