
🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》
✨逆境不吐心中苦,顺境不忘来时路! 🎬 博主简介:

引言:前篇文章,小编已经介绍了关于C++List类的相关知识.接下来我将带领大家继续深入学习C++的相关内容!本篇文章着重介绍关于C++Stack和Queue类以及实现Stack和Queue类的接口,那么这里面到底有哪些知识需要我们去学习的呢?废话不多说,带着这些疑问,下面跟着小编的节奏🎵一起学习吧!
目录
- 1.stack的介绍
- [2. stack类的构造函数(constructor)声明](#2. stack类的构造函数(constructor)声明)
- [3. stack的成员函数](#3. stack的成员函数)
-
- [3.1 empty介绍](#3.1 empty介绍)
- [3.2 size介绍](#3.2 size介绍)
- [3.3 top介绍](#3.3 top介绍)
- [3.4 push介绍](#3.4 push介绍)
- [3.5 emplace介绍](#3.5 emplace介绍)
- [3.6 pop介绍](#3.6 pop介绍)
- [3.7 swap介绍](#3.7 swap介绍)
- 4.关于stack的一些oj题目
- [5. stack核心框架接口的模拟实现](#5. stack核心框架接口的模拟实现)
- [6. queue介绍](#6. queue介绍)
- [7. queue类的构造函数(constructor)声明](#7. queue类的构造函数(constructor)声明)
- [8. queue的成员函数](#8. queue的成员函数)
-
- [8.1 empty介绍](#8.1 empty介绍)
- [8.2 size介绍](#8.2 size介绍)
- [8.3 front介绍](#8.3 front介绍)
- [8.4 back介绍](#8.4 back介绍)
- [8.5 push介绍](#8.5 push介绍)
- [8.6 emplace介绍](#8.6 emplace介绍)
- [8.7 pop介绍](#8.7 pop介绍)
- [8.8 swap介绍](#8.8 swap介绍)
- [9. 关于queue的一些oj题目](#9. 关于queue的一些oj题目)
- [10. queue核心框架接口的模拟实现](#10. queue核心框架接口的模拟实现)
- [11. priority_queue的介绍](#11. priority_queue的介绍)
- [12. priority_queue类的构造函数(constructor)声明](#12. priority_queue类的构造函数(constructor)声明)
- [13. priority_queue的成员函数](#13. priority_queue的成员函数)
-
- [13.1 empty介绍](#13.1 empty介绍)
- [13.2 size介绍](#13.2 size介绍)
- [13.3 top介绍](#13.3 top介绍)
- [13.4 push介绍](#13.4 push介绍)
- [13.5 emplace介绍](#13.5 emplace介绍)
- [13.6 pop介绍](#13.6 pop介绍)
- [13.7 swap介绍](#13.7 swap介绍)
- [14. 关于priority_queue的一些oj题目](#14. 关于priority_queue的一些oj题目)
- [15. priority_queue核心框架接口的模拟实现](#15. priority_queue核心框架接口的模拟实现)
- [16. 适配器介绍](#16. 适配器介绍)
- [17. deque的简单介绍(了解)](#17. deque的简单介绍(了解))
-
- [17.1 为什么选择deque作为stack和queue的底层默认容器?](#17.1 为什么选择deque作为stack和queue的底层默认容器?)
1.stack的介绍
stack的文档介绍
这是C++ 标准库中std::stack的官方文档说明.stack 是C++ STL(标准模板库)中的容器适配器(不是独立容器),它封装了底层容器(默认是 deque),并强制遵循后进先出(LIFO, Last In First Out)的访问规则 ------ 只能从栈顶插入、删除和读取元素,无法随机访问栈中的其他元素,也不支持迭代器遍历.
1️⃣模板定义template <class T, class Container = deque > class stack;
stack是一个模板类 ,有两个参数:
T:栈中存储的元素类型(比如int、string).
Container:底层用来存储数据的容器类型,默认是deque<T>.你也可以手动指定为vector或list.
2️⃣LIFO stack(后进先出栈)核心规则:栈是后进先出(Last-In-First-Out)的容器,元素只能从栈顶 (容器的一端)插入和取出,不能随机访问中间元素.
3️⃣容器适配器的本质
stack不是一个独立的容器,而是一个容器适配器 (container adaptor).它的底层是封装了一个普通容器(比如默认的
deque),然后只暴露符合栈规则的接口.入栈(
push)和出栈(pop)操作,本质是在底层容器的尾部(back)进行插入和删除,这个尾部就是栈的顶部(top).
4️⃣底层容器的要求要作为
stack的底层容器,必须支持以下操作:
empty():判断容器是否为空
size():返回容器元素数量
back():访问容器尾部元素
push_back():在容器尾部插入元素
pop_back():删除容器尾部元素标准库中的
vector、deque、list都满足这些要求,因此都可以作为stack的底层容器.默认情况下,
stack会使用deque,因为它在两端插入/删除的效率很高,是最优选择.
2. stack类的构造函数(constructor)声明
这是C++ 标准库中std::stack构造函数的官方文档说明.
1️⃣构造函数签名explicit stack (const container_type& ctnr = container_type());
explicit关键字 :表示这个构造函数不能用于隐式类型转换,必须显式调用,避免意外的类型转换.
参数ctnr:是底层容器的对象(比如默认的deque<T>),默认值是container_type(),也就是默认构造一个空的底层容器.
版本兼容 :顶部的C++98和C++11标签说明这个构造函数在 C++98 就已存在,并在 C++11 中继续支持.
2️⃣核心功能这个构造函数的作用是创建一个
stack容器适配器对象 .因为
stack是容器适配器,它内部会维护一个底层容器来存储数据:如果构造时传入了
ctnr参数,内部底层容器会是这个参数的副本;如果没有传参数,就默认创建一个空的底层容器(比如默认的
deque).
3️⃣参数详解
ctnr这是底层容器的对象,
container_type是stack模板的第二个参数Container的类型别名(比如默认是deque<T>).你可以通过这个参数,用一个已有的容器来初始化栈.例如:
deque my_deque = {1,2,3};
stack my_stack(my_deque); // 用已有deque初始化栈
alloc这是分配器对象,用于底层容器的内存分配.
这个参数仅在分配器类型满足
uses_allocator::value == true时才会生效,一般日常开发中很少用到.
拷贝构造(隐含说明)文档最后一行提到的"A stack of the same type...",指的是支持用另一个同类型的
stack来构造新的栈(即拷贝构造),例如:stack s1;
s1.push(10);
stack s2(s1); // 用s1拷贝构造s2
3. stack的成员函数
3.1 empty介绍
这是C++ 标准库中std::stack::empty成员函数的官方文档说明.
1️⃣函数签名bool empty() const;
bool:返回值是布尔类型,表示栈是否为空.
const:这是一个只读成员函数 ,调用时不会修改栈的任何内容.
2️⃣核心功能这个函数的作用是判断栈是否为空 (即栈中元素的数量是否为 0).
因为
stack是容器适配器,它的empty()本质上是调用了底层容器 (比如默认的deque)的empty()方法来实现的.
3️⃣参数与返回值
参数 :无(none),调用时不需要传入任何参数.
返回值 :
true:栈为空(底层容器的大小为 0).
false:栈不为空(底层容器包含至少一个元素).
3.2 size介绍
这是C++标准库中std::stack::size成员函数的官方文档说明.
1️⃣函数签名size_type size() const;
size_type:返回值类型,是一个无符号整数类型 (通常和size_t等价).
const:这是一个只读成员函数,调用时不会修改栈的内容.
2️⃣核心功能这个函数的作用是返回栈中当前的元素数量 。
因为
stack是容器适配器,它的size()本质上是调用了底层容器(比如默认的deque)的size()方法来实现的。
3️⃣参数与返回值
参数 :无(none),调用时不需要传入任何参数.
返回值 :返回栈中元素的个数,也就是底层容器的元素数量,类型为无符号整数
size_type.
3.3 top介绍
这是C++标准库中std::stack::top成员函数的官方文档说明.
1️⃣函数签名(两个重载版本)value_type& top();
const value_type& top() const;
value_type& top();:返回栈顶元素的可读写引用 ,用于修改栈顶元素.
const value_type& top() const;:返回栈顶元素的只读引用 ,仅用于读取,不能修改.这两个版本在 C++98 中就已存在,并在 C++11 中继续支持.
2️⃣核心功能这个函数的作用是返回栈顶元素的引用 .
因为栈是后进先出(LIFO)的容器,栈顶元素就是最后插入的那个元素.它本质上是调用了底层容器(比如默认的
deque)的back()方法来实现的.
3️⃣参数与返回值
参数 :无(none),调用时不需要传入任何参数.
返回值 :栈顶元素的引用,非const版本可修改栈顶,const版本仅能读取.
4️⃣关键注意事项调用
top()前,必须先用empty()判断栈是否为空!如果栈为空时调用
top(),会触发未定义行为(通常导致程序崩溃).
3.4 push介绍
这是C++标准库中std::stack::push成员函数的官方文档说明.
1️⃣函数签名void push (const value_type& val);
void:无返回值,执行入栈操作后不返回任何内容.
const value_type& val:参数是要入栈的元素的const引用 ,避免拷贝开销,同时保证不会修改传入的原始值.版本兼容:该函数在 C++98 中就已存在,并在 C++11 中继续支持.
2️⃣核心功能这个函数的作用是在栈顶插入一个新元素 ,新元素的内容是参数
val的副本.因为
stack是容器适配器,它的push()本质上是调用了底层容器(比如默认的deque)的push_back()方法,在容器的尾部(也就是栈的顶部)插入元素.
3️⃣参数与返回值
参数val:要插入的元素值,value_type是栈中存储的元素类型(比如int、string).
返回值 :无(none),仅执行插入操作.
4️⃣关键对比
push()是通过拷贝元素 入栈,如果你想更高效地直接在栈顶构造元素(避免拷贝),可以使用emplace()方法.
3.5 emplace介绍
这是C++ 标准库中std::stack::emplace成员函数的官方文档说明.
1️⃣函数签名template <class... Args> void emplace (Args&&... args);
模板可变参数 :class... Args和Args&&... args是 C++11 引入的可变参数模板和完美转发语法,用来接收任意数量、任意类型的参数,并原封不动地转发给元素的构造函数.
void:无返回值,仅执行构造和插入操作.
2️⃣核心功能这个函数的作用是在栈顶原地构造并插入一个新元素 .
和
push()不同:
push()是先构造一个临时元素.再将其拷贝/移动到栈顶;
emplace()则直接在栈顶的内存位置上,用传入的参数构造元素,完全避免了拷贝或移动的开销,效率更高.因为
stack是容器适配器,它的emplace()本质上是调用底层容器(比如默认的deque)的emplace_back()方法来实现的.
3️⃣参数与返回值
参数args:用于构造新元素的参数,会被完美转发给元素的构造函数.例如,如果栈存储的是自定义类Person(构造函数为Person(string name, int age)),那么emplace("Alice", 20)就会用这两个参数直接构造Person对象.
返回值 :无(none),仅执行构造和插入操作.
4️⃣适用场景当元素的构造需要多个参数时,
emplace()可以直接传递构造参数,代码更简洁;当希望避免拷贝/移动开销、追求更高效率时,优先使用
emplace();如果已经有一个现成的对象需要入栈,使用
push()更直观.
3.6 pop介绍
这是C++标准库中std::stack::pop成员函数的官方文档说明.
1️⃣函数签名void pop();
void:无返回值,执行出栈操作后不返回任何内容.无参数,调用时不需要传入任何值.
2️⃣核心功能这个函数的作用是移除栈顶的元素 ,并将栈的大小减 1.
被移除的元素是最后一个入栈的元素(符合后进先出规则),它的值可以在调用
pop()前通过stack::top()获取.因为
stack是容器适配器,它的pop()本质上是调用了底层容器(比如默认的deque)的pop_back()方法,删除容器尾部(栈顶)的元素.元素被移除时,会调用该元素的析构函数,释放其占用的资源.
3️⃣参数与返回值
参数 :无(none).
返回值 :无(none),仅执行删除操作.
4️⃣关键注意事项⚠️ 最容易踩的坑 :
①**
pop()不返回被删除的元素**:新手常误以为pop()会返回栈顶值,实际它仅删除元素.如果需要获取被删除的值,必须先调用top()读取,再调用pop()删除.②空栈调用
pop()会崩溃 :调用pop()前,必须先用empty()判断栈是否为空,否则会触发未定义行为(通常导致程序崩溃).
5️⃣总结
pop()仅负责删除栈顶元素 ,不返回任何值.必须配合
top()才能获取并删除元素,且调用前必须用empty()判空.底层依赖容器的
pop_back()实现,会触发元素的析构函数.
3.7 swap介绍
这是C++标准库中
std::stack::swap成员函数的官方文档说明.
1️⃣函数签名void swap (stack& x) noexcept(/see below /);
void:无返回值,仅执行交换操作.
stack& x:参数是另一个同类型栈的引用,用来和当前栈交换内容.
noexcept(/*see below*/):该函数是否抛出异常,取决于底层容器的swap操作是否是noexcept(比如默认底层容器deque的swap是noexcept,因此stack的swap也会是noexcept).
2️⃣核心功能这个函数的作用是交换当前栈和参数栈
x的全部内容 .因为
stack是容器适配器,它的swap本质上是调用了底层容器的非成员swap函数,直接交换两个栈的底层容器(比如默认的deque),这个过程通常是 O(1) 时间复杂度(仅交换底层容器的内部指针,不拷贝元素),效率非常高.
3️⃣参数与返回值
参数x:另一个同类型的stack对象(即元素类型T和底层容器类型Container都必须完全一致).两个栈的大小可以不同.
返回值 :无(none),仅执行交换操作.
4️⃣关键注意事项⚠️ 必须是同类型栈 :只有当两个栈的模板参数(元素类型
T和底层容器Container)完全相同时,才能调用swap.例如:✅ 合法:
stack<int>和stack<int>(默认底层都是deque)❌ 非法:
stack<int>和stack<int, vector<int>>(底层容器不同)
5️⃣总结
swap()用于高效交换两个同类型栈的内容,底层依赖容器的swap实现,通常是 O(1) 复杂度.要求两个栈的元素类型和底层容器类型必须完全一致.
是否抛出异常由底层容器的
swap操作决定.
4.关于stack的一些oj题目
4.1最小栈
cpp
class MinStack {
public:
MinStack() {
}
void push(int val) {
_st.push(val);
if(_minst.empty() || val <= _minst.top())
_minst.push(val);
}
void pop() {
if(_st.top() == _minst.top())
_minst.pop();
_st.pop();
}
int top() {
return _st.top();
}
int getMin() {
return _minst.top();
}
private:
stack<int> _st;
stack<int> _minst;
};
4.2栈的压入、弹出序列
cpp
class Solution {
public:
bool IsPopOrder(vector<int>& pushV, vector<int>& popV) {
stack<int> st;
size_t pushi = 0, popi = 0;
while (pushi < pushV.size()) {
// 入栈序列入栈
st.push(pushV[pushi++]);
// 栈顶尝试跟出栈序列匹配
while (!st.empty() && st.top() == popV[popi]) {
popi++;
st.pop();
}
}
return st.empty();
}
};//1.pushi 指向数据入栈
//2.持续让栈里面的数据跟popi指向出栈比较相等,则 popi++,出栈顶数据,直到栈为空或者不匹配
//3.结束条件:pushi 走到尾
4.3验证栈序列
cpp
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
stack<int> st;
int j = 0; // 指向popped序列的当前元素
for (int num : pushed) {
st.push(num);
// 循环检查栈顶是否匹配当前popped元素
while (!st.empty() && st.top() == popped[j]) {
st.pop();
j++;
}
}
// 栈为空说明所有元素都按顺序弹出
return st.empty();
}
};
4.4用栈操作构建数组
cpp
class Solution {
public:
vector<string> buildArray(vector<int>& target, int n) {
vector<string> res;
int i = 0; // 指向目标数组的当前元素
for (int num = 1; num <= n && i < target.size(); num++) {
res.push_back("Push");
if (num == target[i]) {
// 当前数字是目标元素,保留在栈中
i++;
} else {
// 当前数字不是目标元素,弹出栈
res.push_back("Pop");
}
}
return res;
}
};
4.5栈排序
cpp
class SortedStack {
private:
stack<int> _main; // 主栈:保存有序元素(栈顶最小)
stack<int> _temp; // 临时栈:辅助排序
public:
SortedStack() {
// 构造函数:默认初始化两个栈
}
void push(int val) {
// 把主栈中比val小的元素移到临时栈
while (!_main.empty() && _main.top() < val) {
_temp.push(_main.top());
_main.pop();
}
// 插入当前元素
_main.push(val);
// 把临时栈的元素移回主栈
while (!_temp.empty()) {
_main.push(_temp.top());
_temp.pop();
}
}
void pop() {
if (!_main.empty()) {
_main.pop();
}
}
int peek() {
return _main.empty() ? -1 : _main.top();
}
bool isEmpty() {
return _main.empty();
}
};
5. stack核心框架接口的模拟实现
cpp
#include <iostream>
#include <deque> // 默认底层容器
#include <vector> // 可替换的底层容器示例
using namespace std;
// 模板参数:T-存储的元素类型,Container-底层容器类型(默认deque)
template <class T, class Container = deque<T>>
class Stack {
public:
// 1. 判空:调用底层容器的empty()
bool empty() const {
return _con.empty();
}
// 2. 获取大小:调用底层容器的size()
size_t size() const {
return _con.size();
}
// 3. 获取栈顶元素:调用底层容器的back()(栈顶对应容器尾部)
T& top() {
return _con.back();
}
// const版本:只读栈顶
const T& top() const {
return _con.back();
}
// 4. 入栈:调用底层容器的push_back()
void push(const T& val) {
_con.push_back(val);
}
// C++11 右值引用版本(优化拷贝)
void push(T&& val) {
_con.push_back(move(val));
}
// 5. 出栈:调用底层容器的pop_back()(仅删除,无返回值)
void pop() {
if (!empty()) { // 空栈保护(可选,标准库不做保护,调用pop空栈会崩溃)
_con.pop_back();
}
}
private:
Container _con; // 封装底层容器,对外隐藏实现细节
};
int main() {
// 1. 默认底层容器(deque)
Stack<int> st1;
st1.push(10);
st1.push(20);
st1.push(30);
cout << "栈大小:" << st1.size() << endl; // 输出 3
cout << "栈顶元素:" << st1.top() << endl; // 输出 30
st1.pop();
cout << "出栈后栈顶:" << st1.top() << endl; // 输出 20
// 2. 自定义底层容器(vector)
Stack<int, vector<int>> st2;
st2.push(100);
st2.push(200);
cout << "vector底层栈顶:" << st2.top() << endl; // 输出 200
return 0;
}
6. queue介绍
这是C++标准库中std::queue的官方文档说明.
1️⃣核心定义:FIFO 队列
std::queue是一个先进先出(FIFO, First-In First-Out)的容器适配器,元素从一端(队尾)插入,从另一端(队首)取出,符合"先入队的元素先出队"的规则.
2️⃣本质:容器适配器
和std::stack一样,queue本身不直接存储数据,而是封装了一个底层容器 (默认是std::deque),所有操作都通过调用底层容器的接口实现:入队(
push)→ 底层容器的push_back()(队尾插入)出队(
pop)→ 底层容器的pop_front()(队首删除)队首元素(
front)→ 底层容器的front()队尾元素(
back)→ 底层容器的back()
3️⃣底层容器的要求底层容器必须支持以下操作:
empty():判断容器是否为空
size():获取元素个数
front():访问队首元素
back():访问队尾元素
push_back():在尾部插入元素
pop_front():在头部删除元素标准库中,
std::deque和std::list都满足这些要求,因此queue默认使用std::deque作为底层容器(兼顾插入/删除效率).
4️⃣模板定义template <class T, class Container = deque> class queue;
T:队列存储的元素类型(如int、string)
Container:底层容器类型,默认是deque<T>,也可以指定为list<T>等符合要求的容器.
核心逻辑总结
| queue 接口 | 底层容器接口 | 说明 |
|---|---|---|
push(val) |
_con.push_back(val) |
入队:队尾插入元素 |
pop() |
_con.pop_front() |
出队:删除队首元素(无返回值) |
front() |
_con.front() |
获取队首元素 |
back() |
_con.back() |
获取队尾元素 |
empty() |
_con.empty() |
判断队列是否为空 |
size() |
_con.size() |
获取队列元素个数 |
7. queue类的构造函数(constructor)声明
这是C++标准库中std::queue构造函数的官方文档.
1️⃣构造函数签名explicit queue (const container_type& ctnr = container_type());
explicit:表示这个构造函数不能用于隐式类型转换,必须显式调用(例如不能写queue<int> q = my_deque;,必须写queue<int> q(my_deque);).
container_type:是底层容器的类型别名,对应模板参数Container(默认是std::deque<T>).
ctnr:可选参数,是一个底层容器对象的引用.如果传入,队列的内部容器会是这个对象的副本;如果不传入,默认会创建一个空的底层容器.
2️⃣核心逻辑队列是容器适配器 ,内部会维护一个底层容器对象:
如果你在构造时传入了一个底层容器(如
deque或list),队列会复制这个容器的内容作为初始元素.如果不传入参数,队列会默认初始化一个空的底层容器.
3️⃣参数说明
| 参数 | 含义 |
|---|---|
ctnr |
底层容器对象,用于初始化队列的内部存储。例如传入一个 deque<int>,队列就会复制这个 deque 的元素作为初始队列。 |
alloc |
C++11 新增的分配器参数,用于自定义内存分配,一般使用默认值即可。 |
x |
同类型的队列对象,用于拷贝构造(文档中这个参数对应拷贝构造函数)。 |
8. queue的成员函数
8.1 empty介绍
这是C++ 标准库中std::queue::empty成员函数的官方文档.
1️⃣函数签名bool empty() const;
const修饰:表示这个函数不会修改队列的状态,是只读操作.返回值:
bool类型,true表示队列为空,false表示队列非空.
2️⃣核心功能这个函数用于判断队列是否为空 (即队列中是否没有元素),本质是检查队列的大小是否为 0.
3️⃣实现原理因为
std::queue是容器适配器,empty()函数实际上是直接调用底层容器的empty()方法 (比如默认底层容器std::deque的empty()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :
true:如果底层容器的大小为 0(队列为空).
false:否则(队列中有元素).
5️⃣常见使用场景在调用
pop()(出队)或front()(访问队首)之前,通常会先用empty()判断队列是否为空,避免空队列操作导致未定义行为(比如崩溃).
8.2 size介绍
这是C++ 标准库中std::queue::size成员函数的官方文档.
1️⃣函数签名size_type size() const;
const修饰 :表示这个函数不会修改队列的状态,是只读操作.
size_type:是一个无符号整数类型的别名(通常是size_t),用来表示容器的大小.
2️⃣核心功能这个函数用于获取队列中当前的元素数量 .
3️⃣实现原理因为
std::queue是容器适配器,size()函数实际上是直接调用底层容器的size()方法 (比如默认底层容器std::deque的size()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :返回底层容器中的元素数量,也就是队列的大小.
注意:
size_type是无符号类型,所以永远不会是负数.
5️⃣常见使用场景结合
empty()判断队列状态(比如if (!q.empty()) { ... }).在遍历队列或批量处理元素前,了解队列的元素总数.
8.3 front介绍
这是C++ 标准库中std::queue::front成员函数的官方文档.
1️⃣函数签名value_type& front();
const value_type& front() const;
提供了两个版本:
非
const版本:返回队首元素的可修改引用 ,可以直接修改队首元素的值.
const版本:返回队首元素的只读引用 ,只能读取,不能修改.
value_type是队列存储元素的类型(比如queue<int>中就是int).
2️⃣核心功能这个函数用于访问队列的队首元素 ,也就是队列中"最早入队"的元素,也是下一个会被
pop()弹出的元素(符合 FIFO 特性).
3️⃣实现原理因为
std::queue是容器适配器,front()函数实际上是直接调用底层容器的front()方法 (比如默认底层容器std::deque的front()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :返回队首元素的引用.
注意:如果队列为空时调用
front(),会触发未定义行为(比如程序崩溃),因此通常会先用empty()判断队列是否为空.
5️⃣常见使用场景在
pop()出队前,先获取队首元素的值.直接修改队首元素(仅当队列是非
const时).
8.4 back介绍
这是C++ 标准库中std::queue::back成员函数的官方文档.
1️⃣函数签名value_type& back();
const value_type& back() const;
提供两个版本:
非
const版本:返回队尾元素的可修改引用 ,可以直接修改队尾元素的值.
const版本:返回队尾元素的只读引用 ,只能读取,不能修改.
value_type是队列存储元素的类型(例如queue<int>中就是int).
2️⃣核心功能这个函数用于访问队列的队尾元素 ,也就是队列中"最新入队"的元素(最后一个被
push()入队的元素).
3️⃣实现原理因为
std::queue是容器适配器,back()函数实际上是直接调用底层容器的back()方法 (比如默认底层容器std::deque的back()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :返回队尾元素的引用。
注意:如果队列为空时调用
back(),会触发未定义行为(比如程序崩溃),因此通常会先用empty()判断队列是否为空.
5️⃣常见使用场景在
push()入队后,获取最新入队的元素.直接修改队尾元素(仅当队列是非
const时).
8.5 push介绍
这是C++ 标准库中std::queue::push成员函数的官方文档.
1️⃣函数签名void push (const value_type& val);
const value_type& val:传入要插入的元素的常量引用 ,避免不必要的拷贝(C++11 还支持右值引用版本push(value_type&& val)来进一步优化).
value_type是队列存储元素的类型(例如queue<int>中就是int).返回值:
void,无返回值.
2️⃣核心功能这个函数用于在队列的队尾插入一个新元素 ,新元素会成为队列的最后一个元素(最新入队的元素)),符合队列"先进先出(FIFO)"的特性.
3️⃣实现原理因为
std::queue是容器适配器,push()函数实际上是直接调用底层容器的push_back()方法 (比如默认底层容器std::deque的push_back()),时间复杂度为 O(1) (deque的尾部插入是常数时间).
4️⃣参数与返回值
参数 :val,要插入的元素的值,元素会被初始化为此值.
返回值 :无(none).
5️⃣常见使用场景向队列中添加新元素,是实现队列"入队"操作的核心接口.
与
pop()(出队)、front()(访问队首)配合,实现完整的队列操作.
8.6 emplace介绍
这是C++标准库中std::queue::emplace成员函数的官方文档.
1️⃣函数签名template <class... Args> void emplace (Args&&... args);
模板参数Args&&... args:可变参数模板,用于接收构造新元素所需的任意参数(右值引用转发,避免拷贝).返回值:
void,无返回值.
2️⃣核心功能这个函数用于在队列的队尾****原地构造 一个新元素,而不是先创建元素再拷贝/移动到队列中.新元素会直接在底层容器的内存空间中构造,因此比
push更高效(减少了一次拷贝/移动操作).
3️⃣实现原理因为
std::queue是容器适配器,emplace()函数实际上是直接调用底层容器的emplace_back()方法 (比如默认底层容器std::deque的emplace_back()),并将参数args转发给新元素的构造函数,时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :args,构造新元素所需的参数(会被转发给元素的构造函数).
返回值 :无(none).
5️⃣常见使用场景当队列存储的是自定义类对象 时,
emplace可以避免临时对象的创建和拷贝,提升性能.需要直接用构造函数参数入队时,
emplace比push更简洁(无需显式创建对象).
8.7 pop介绍
这是C++ 标准库中std::queue::pop成员函数的官方文档.
1️⃣函数签名void pop();
无参数,无返回值.
注意:
pop()仅删除队首元素,不会返回被删除的元素值 .如果需要获取元素值,必须先用front()读取,再调用pop()删除.
2️⃣核心功能这个函数用于移除队列的队首元素 (也就是队列中"最早入队"的元素,与
front()访问的是同一个元素),队列的大小会减 1.被移除的元素会调用其析构函数释放资源.
3️⃣实现原理因为
std::queue是容器适配器,pop()函数实际上是直接调用底层容器的pop_front()方法 (比如默认底层容器std::deque的pop_front()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :无(none).
5️⃣常见注意事项
空队列保护 :如果队列为空时调用pop(),会触发未定义行为(比如程序崩溃),因此必须先用empty()判断队列是否为空.
与front()配合 :因为pop()不返回值,所以通常会先调用front()获取队首元素的值,再调用pop()删除元素.
8.8 swap介绍
这是 C++ 标准库中std::queue::swap成员函数的官方文档.
1️⃣函数签名void swap (queue& x) noexcept(/see below /);
参数queue& x:另一个同类型的队列(模板参数T和底层容器Container必须完全一致),用于和当前队列交换内容.
noexcept:是否为 noexcept 取决于底层容器的swap操作是否为 noexcept(比如默认底层容器deque的swap是 noexcept,因此队列的swap也是 noexcept).返回值:
void,无返回值.
2️⃣核心功能这个函数用于交换当前队列与另一个同类型队列的全部内容 ,本质是交换它们的底层容器(因为
queue是容器适配器).交换后,两个队列的元素会完全互换,且时间复杂度由底层容器的swap决定(通常是 O(1) ,比如deque的swap仅交换内部指针).
3️⃣实现原理因为
std::queue是容器适配器,swap()函数实际上是调用底层容器的非成员swap函数 ,直接交换两个队列的底层容器对象,因此效率极高(无需拷贝元素).
4️⃣参数与返回值
参数 :x,另一个同类型的队列(模板参数必须完全匹配),大小可以不同.
返回值 :无(none).
5️⃣常见使用场景需要快速交换两个队列的内容,比逐个元素拷贝高效得多(时间复杂度通常为 O(1)).
在算法中临时交换队列状态,或用于容器适配器的高效重置.
9. 关于queue的一些oj题目
9.1用栈实现队列
cpp
class MyQueue {
private:
stack<int> inStack; // 输入栈:负责入队
stack<int> outStack; // 输出栈:负责出队/查看队首
// 将输入栈的元素转移到输出栈(仅当输出栈为空时调用)
void transfer() {
if (outStack.empty()) {
while (!inStack.empty()) {
outStack.push(inStack.top());
inStack.pop();
}
}
}
public:
MyQueue() {
// 构造函数:默认初始化两个栈
}
void push(int x) {
// 入队:直接压入输入栈
inStack.push(x);
}
int pop() {
// 出队:先确保输出栈有元素,再弹出栈顶
transfer();
int val = outStack.top();
outStack.pop();
return val;
}
int peek() {
// 查看队首:先确保输出栈有元素,再返回栈顶
transfer();
return outStack.top();
}
bool empty() {
// 队列为空:两个栈都为空
return inStack.empty() && outStack.empty();
}
};
9.2用队列实现栈
cpp
class MyStack {
private:
queue<int> q1; // 主队列:存储栈元素,队首 = 栈顶
queue<int> q2; // 临时队列:辅助调整元素顺序
public:
MyStack() {
// 构造函数:默认初始化两个队列
}
void push(int x) {
// 1. 新元素先入队到临时队列q2
q2.push(x);
// 2. 把主队列q1的所有元素移到q2,让新元素到队首
while (!q1.empty()) {
q2.push(q1.front());
q1.pop();
}
// 3. 交换q1和q2,让q1成为主队列
swap(q1, q2);
}
int pop() {
// 队首就是栈顶,直接弹出
int val = q1.front();
q1.pop();
return val;
}
int top() {
// 队首就是栈顶,直接返回
return q1.front();
}
bool empty() {
// 主队列为空则栈为空
return q1.empty();
}
};
9.3设计循环双端队列
cpp
class MyCircularDeque {
private:
vector<int> data;
int front;
int rear;
int capacity;
public:
MyCircularDeque(int k) {
// 数组容量设为k+1,预留一个空位区分空/满
capacity = k + 1;
data.resize(capacity);
front = 0;
rear = 0;
}
bool insertFront(int value) {
if (isFull()) {
return false;
}
// 队首插入:front向前移动一位(循环)
front = (front - 1 + capacity) % capacity;
data[front] = value;
return true;
}
bool insertLast(int value) {
if (isFull()) {
return false;
}
// 队尾插入:先赋值,再移动rear
data[rear] = value;
rear = (rear + 1) % capacity;
return true;
}
bool deleteFront() {
if (isEmpty()) {
return false;
}
// 队首删除:front向后移动一位(循环)
front = (front + 1) % capacity;
return true;
}
bool deleteLast() {
if (isEmpty()) {
return false;
}
// 队尾删除:rear向前移动一位(循环)
rear = (rear - 1 + capacity) % capacity;
return true;
}
int getFront() {
if (isEmpty()) {
return -1;
}
return data[front];
}
int getRear() {
if (isEmpty()) {
return -1;
}
// rear指向队尾的下一个位置,所以取前一个位置
return data[(rear - 1 + capacity) % capacity];
}
bool isEmpty() {
return front == rear;
}
bool isFull() {
return (rear + 1) % capacity == front;
}
};
9.4设计前中后队列
cpp
class FrontMiddleBackQueue {
private:
deque<int> left;
deque<int> right;
// 平衡两个队列的大小
void balance() {
// left比right多2个时,将left的最后一个移到right前面
if (left.size() > right.size() + 1) {
right.push_front(left.back());
left.pop_back();
}
// right比left多1个时,将right的第一个移到left后面
else if (right.size() > left.size()) {
left.push_back(right.front());
right.pop_front();
}
}
public:
FrontMiddleBackQueue() {}
void pushFront(int val) {
left.push_front(val);
balance();
}
void pushMiddle(int val) {
// 中间插入到left的队尾
if (left.size() > right.size()) {
right.push_front(left.back());
left.pop_back();
}
left.push_back(val);
balance();
}
void pushBack(int val) {
right.push_back(val);
balance();
}
int popFront() {
if (left.empty() && right.empty()) return -1;
int val;
if (!left.empty()) {
val = left.front();
left.pop_front();
} else {
val = right.front();
right.pop_front();
}
balance();
return val;
}
int popMiddle() {
if (left.empty() && right.empty()) return -1;
int val = left.back();
left.pop_back();
balance();
return val;
}
int popBack() {
if (left.empty() && right.empty()) return -1;
int val;
if (!right.empty()) {
val = right.back();
right.pop_back();
} else {
val = left.back();
left.pop_back();
}
balance();
return val;
}
};
9.5有序队列
核心解题思路(分两步拆解)
第一步:先吃透题目中的操作定义题目要求的操作是:每次可以选择字符串前
k个字符中的任意一个,将其移动到字符串末尾 .这个操作的自由度完全由
k决定------k越小,操作的限制越大;k越大,操作的自由度越高.
第二步:分情况分析操作的自由度,推导解法
情况 1:k == 1(操作自由度极低)因为只能选第一个字符移到末尾(前1个字符里只有这一个选择),所以操作本质是生成原字符串的循环移位 .
例如:
s = "cba",操作只能得到:第1次操作:
"cba"→ 移第一个字符c到末尾 →"bac"第2次操作:
"bac"→ 移第一个字符b到末尾 →"acb"第3次操作:
"acb"→ 移第一个字符a到末尾 →"cba"(回到原字符串)结论:
k=1时无法调整字符的相对顺序 ,只能遍历所有n种循环移位结果,选字典序最小的.
情况 2:k ≥ 2(操作自由度足够高)这是本题的核心难点,关键结论是:当
k≥2时,能通过多次操作实现字符的任意重排(等价于排序) .为什么?举个直观例子(以
s = "bac",k=2为例):我们想把
"bac"变成"abc"(最小字典序),操作步骤:①选前2个字符中的
a(第二个字符)移到末尾 →"bca"②选前2个字符中的
b(第一个字符)移到末尾 →"cab"③选前2个字符中的
a(第二个字符)移到末尾 →"cba"④选前2个字符中的
b(第二个字符)移到末尾 →"cab"(换个思路)更本质的逻辑:
k≥2时,能实现相邻字符的交换(类似冒泡排序),而能交换相邻字符就意味着可以对整个字符串排序.结论:
k≥2时,直接对字符串排序就能得到最小字典序(无需复杂操作).
总结这道题的解题思路核心是根据
k的值判断操作自由度 :①
k=1:操作受限,只能生成循环移位,遍历所有移位结果选最小;②
k≥2:操作无本质限制,直接排序得到最小字典序.
cpp
class Solution {
public:
string orderlyQueue(string s, int k) {
if (k == 1) {
// 生成所有循环移位,取字典序最小
string res = s;
int n = s.size();
for (int i = 1; i < n; ++i) {
string temp = s.substr(i) + s.substr(0, i);
if (temp < res) {
res = temp;
}
}
return res;
} else {
// k >= 2 时,直接排序得到最小字典序
sort(s.begin(), s.end());
return s;
}
}
};
10. queue核心框架接口的模拟实现
cpp
#include <iostream>
#include <deque> // 默认底层容器
#include <list>
using namespace std;
// 模板参数:T-元素类型,Container-底层容器类型(默认deque<T>)
template <class T, class Container = deque<T>>
class Queue {
public:
// 1. 判空:调用底层容器的empty(),const修饰保证只读
bool empty() const {
return _con.empty();
}
// 2. 获取大小:调用底层容器的size(),返回无符号整数
size_t size() const {
return _con.size();
}
// 3. 访问队首元素(可修改版本)
T& front() {
return _con.front();
}
// 4. 访问队首元素(只读版本,const对象调用)
const T& front() const {
return _con.front();
}
// 5. 访问队尾元素(可修改版本)
T& back() {
return _con.back();
}
// 6. 访问队尾元素(只读版本,const对象调用)
const T& back() const {
return _con.back();
}
// 7. 入队(队尾插入):调用底层容器的push_back()
void push(const T& val) {
_con.push_back(val);
}
// C++11 右值引用版本(优化拷贝,避免临时对象)
void push(T&& val) {
_con.push_back(move(val));
}
// 8. 出队(队首删除):仅删除,无返回值;空队列保护(可选)
void pop() {
if (!empty()) { // 标准库不做保护,空队列pop会崩溃,此处增加容错
_con.pop_front();
}
}
// 9. 交换两个队列的内容:直接交换底层容器
void swap(Queue& other) noexcept {
_con.swap(other._con); // 调用底层容器的swap
}
private:
Container _con; // 封装底层容器,对外隐藏实现细节
};
// 全局swap函数(适配标准库swap接口)
template <class T, class Container>
void swap(Queue<T, Container>& q1, Queue<T, Container>& q2) noexcept {
q1.swap(q2);
}
int main() {
// 1. 默认底层容器(deque)
Queue<int> q1;
q1.push(10);
q1.push(20);
q1.push(30);
cout << "队列大小:" << q1.size() << endl; // 输出 3
cout << "队首元素:" << q1.front() << endl; // 输出 10
cout << "队尾元素:" << q1.back() << endl; // 输出 30
q1.pop(); // 出队(删除队首10)
cout << "出队后队首:" << q1.front() << endl; // 输出 20
// 2. 自定义底层容器(list)
Queue<int, list<int>> q2;
q2.push(100);
q2.push(200);
cout << "list底层队列队尾:" << q2.back() << endl; // 输出 200
// 3. 交换两个队列
Queue<int> q3;
q3.push(1000);
swap(q1, q3);
cout << "交换后q1队首:" << q1.front() << endl; // 输出 1000
return 0;
}
11. priority_queue的介绍
这是C++ 标准库中std::priority_queue的官方文档说明.
1️⃣本质:带优先级的容器适配器
std::priority_queue是一个容器适配器 ,它不直接存储数据,而是封装了一个底层容器(默认是std::vector),并通过堆(heap)算法维护元素的优先级顺序,保证队首(堆顶)始终是优先级最高的元素(默认是最大的元素).
2️⃣模板定义template <class T, class Container = vector, class Compare = less>
class priority_queue;
T:队列存储的元素类型(如int、string).
Container:底层容器类型(默认是std::vector,也支持std::deque),需要支持随机访问迭代器.
Compare:优先级比较规则(默认是less<T>,即构建大顶堆 ,队首是最大元素;若改为greater<T>,则构建小顶堆 ,队首是最小元素).
3️⃣核心特性
优先级保证 :队首元素始终是当前队列中优先级最高的元素(默认按"小于"比较,最大的元素优先级最高).
堆结构维护 :插入/删除元素时,自动调用堆算法(make_heap、push_heap、pop_heap)维护堆的结构,时间复杂度为O(log n).
访问限制 :只能访问队首(堆顶)元素,无法随机访问其他位置的元素.
4️⃣底层容器的要求底层容器必须满足两个条件:
①支持随机访问迭代器 :因为堆算法需要随机访问元素来维护堆结构.
②支持核心操作 :必须提供
empty()、size()、front()、push_back()、pop_back()这些接口.标准库中,
std::vector和std::deque都满足这些要求,因此默认使用std::vector作为底层容器.5️⃣核心接口的实现逻辑
| priority_queue 接口 | 底层容器 + 堆算法 | 说明 |
|---|---|---|
push(val) |
先调用 _con.push_back(val),再调用 push_heap(_con.begin(), _con.end(), _comp) |
插入元素并维护堆结构 |
pop() |
先调用 pop_heap(_con.begin(), _con.end(), _comp)(将堆顶移到容器尾部),再调用 _con.pop_back() |
删除堆顶元素并维护堆结构 |
top() |
返回 _con.front() |
访问堆顶(队首)元素 |
empty() |
调用 _con.empty() |
判断队列是否为空 |
size() |
调用 _con.size() |
获取元素个数 |
6️⃣关键设计思想
适配器模式 :通过封装底层容器和堆算法,对外提供"优先队列"的抽象接口,隐藏了堆的实现细节.
灵活性 :支持自定义底层容器(如deque)和比较规则(如小顶堆),适配不同的业务场景.
12. priority_queue类的构造函数(constructor)声明
这是C++标准库中std::priority_queue构造函数的官方文档.
1️⃣构造函数的核心版本文档中展示了两个主要的构造函数版本:
①初始化版本(版本1)
explicit priority_queue (const Compare& comp = Compare(), const Container& ctnr = Container());
explicit修饰 :表示不能通过隐式类型转换创建队列,必须显式调用(例如不能写priority_queue<int> q = my_vector;,必须写priority_queue<int> q(my_comp, my_vector);).
参数默认值 :
comp:默认是Compare()(默认比较规则为less<T>,即构建大顶堆).
ctnr:默认是Container()(默认底层容器为vector<T>).
核心逻辑 :复制传入的比较对象
comp和底层容器ctnr.调用
make_heap算法,将底层容器转换为堆结构,保证队列初始就是合法的堆.②范围初始化版本(版本2)
template
priority_queue (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Container& ctnr = Container());
迭代器范围 :接收输入迭代器[first, last),将该范围内的元素插入到底层容器.
后续逻辑 :插入元素后,再调用make_heap构造成堆,避免了逐个push的O(n log n)复杂度,直接通过make_heap达到O(n)时间复杂度.
2️⃣参数详细解释
| 参数 | 含义 |
|---|---|
comp |
堆的排序比较对象,可以是函数指针或函数对象,需满足严格弱序 规则。默认是 less<T>(大顶堆),若传入 greater<T> 则构建小顶堆。 |
ctnr |
底层容器对象,用于存储队列元素。默认是 vector<T>,也可以传入 deque<T> 等支持随机访问迭代器的容器。 |
first, last |
输入迭代器,指向要插入的元素范围 [first, last),元素会先被插入到底层容器,再构造为堆。 |
3️⃣底层实现逻辑
无论哪个版本的构造函数,最终都会执行以下步骤:
①初始化内部的比较对象和底层容器(复制传入的
comp和ctnr).②若使用范围版本,先将迭代器范围内的元素插入底层容器.
③调用
make_heap算法,将底层容器转换为堆结构,保证队列初始状态就是合法的堆.
13. priority_queue的成员函数
优先级队列默认使用vector作为其底层存储数据的容器,在vector上又使用了堆算法将vector中
元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priority_queue.注意:默认情况下priority_queue是大堆.
13.1 empty介绍
这是C++标准库中std::priority_queue::empty成员函数的官方文档.
1️⃣函数签名bool empty() const;
const修饰 :表示该函数是只读操作,不会修改优先队列的状态.返回值:
bool类型,true表示队列为空,false表示队列非空.
2️⃣核心功能这个函数用于判断优先队列是否为空 (即队列中元素数量是否为 0),是队列操作前的重要安全检查.
3️⃣实现原理因为
std::priority_queue是容器适配器,empty()函数实际上是直接调用底层容器的empty()方法 (比如默认底层容器std::vector的empty()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :
true:如果底层容器的大小为 0(队列为空).
false:否则(队列中有元素).
5️⃣常见使用场景在调用
top()(访问队首)或pop()(删除队首)之前,通常会先用empty()判断队列是否为空,避免空队列操作导致未定义行为(比如程序崩溃).
13.2 size介绍
这是C++标准库中std::priority_queue::size成员函数的官方文档.
1️⃣函数签名size_type size() const;
const修饰 :表示该函数是只读操作,不会修改优先队列的状态.
size_type:是无符号整数类型的别名(通常为size_t),用于表示容器的元素数量.
2️⃣核心功能这个函数用于获取优先队列中当前的元素总数 ,反映队列的存储规模.
3️⃣实现原理因为
std::priority_queue是容器适配器,size()函数实际上是直接调用底层容器的size()方法 (比如默认底层容器std::vector的size()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :返回底层容器中的元素数量,即优先队列的大小.
注意:
size_type是无符号类型,因此返回值永远不会为负数.
5️⃣常见使用场景结合
empty()判断队列的状态(例如if (pq.size() > 0) { ... }).在批量处理元素前,了解队列的元素总数,规划处理逻辑.
13.3 top介绍
这是C++ 标准库中std::priority_queue::top成员函数的官方文档.
1️⃣函数签名const value_type& top() const;
两个const修饰 :第一个
const:返回值是常量引用 ,表示不能通过该引用修改队首元素(保证堆结构不被意外破坏).第二个
const:成员函数是只读操作 ,不会修改优先队列的状态.
value_type:是队列存储元素的类型(例如priority_queue<int>中就是int).
2️⃣核心功能这个函数用于访问优先队列的队首(堆顶)元素 ,该元素是当前队列中优先级最高的元素(默认大顶堆中是最大的元素),也是下一个被
pop()删除的元素.
3️⃣实现原理因为
std::priority_queue是容器适配器,top()函数实际上是直接调用底层容器的front()方法 (比如默认底层容器std::vector的front()),时间复杂度为 O(1) .
4️⃣参数与返回值
参数 :无(none).
返回值 :返回队首元素的常量引用。
注意:如果队列为空时调用
top(),会触发未定义行为(比如程序崩溃),因此必须先用empty()判断队列是否为空.
5️⃣常见使用场景在调用
pop()(删除队首)之前,先通过top()获取队首元素的值(因为pop()仅删除元素,不返回值).查看当前队列中优先级最高的元素,而不修改队列状态.
13.4 push介绍
这是C++标准库中std::priority_queue::push成员函数的官方文档.
1️⃣函数签名void push (const value_type& val);
const value_type& val:传入要插入的元素的常量引用 ,避免不必要的拷贝(C++11 还支持右值引用版本push(value_type&& val)进一步优化).
value_type:是队列存储元素的类型(例如priority_queue<int>中就是int).返回值:
void,无返回值.
2️⃣核心功能这个函数用于向优先队列中插入一个新元素 ,插入后会自动维护堆的结构,保证队首(堆顶)始终是优先级最高的元素(默认大顶堆中是最大的元素).
3️⃣实现原理因为
std::priority_queue是容器适配器,push()函数的底层执行两步操作:①插入底层容器 :调用底层容器的
push_back()(比如默认底层容器std::vector的push_back()),将新元素添加到容器的末尾。②维护堆结构 :调用
push_heap算法,对整个容器的元素进行调整,使其重新满足堆的性质.时间复杂度为 O(log n) ,因为
push_heap是对数时间复杂度的操作.
4️⃣参数与返回值
参数 :val,要插入的元素的值,元素会被初始化为此值.
返回值 :无(none).
5️⃣常见使用场景向优先队列中添加元素,用于任务调度(高优先级任务优先执行)、TopK 问题(维护前 K 大/小元素)等场景.与
top()、pop()配合,实现完整的优先队列操作.
13.5 emplace介绍
这是C++ 标准库中std::priority_queue::emplace成员函数的官方文档.
1️⃣函数签名template <class... Args> void emplace (Args&&... args);
可变参数模板Args&&... args:接收构造新元素所需的任意参数(通过右值引用转发,避免不必要的拷贝).返回值:
void,无返回值.
2️⃣核心功能这个函数用于在优先队列的底层容器中原地构造一个新元素 ,并自动维护堆的结构,保证队首(堆顶)始终是优先级最高的元素.
和
push相比,emplace不需要先创建元素再拷贝/移动到底层容器,而是直接在容器的内存空间中构造元素,因此减少了一次拷贝/移动操作,效率更高.
3️⃣实现原理因为
std::priority_queue是容器适配器,emplace()函数的底层执行两步操作:①原地构造元素 :调用底层容器的
emplace_back()(比如默认底层容器std::vector的emplace_back()),将参数args转发给新元素的构造函数,在容器尾部原地构造元素.②维护堆结构 :调用
push_heap算法,对整个容器的元素进行调整,使其重新满足堆的性质.时间复杂度为 O(log n) ,因为
push_heap是对数时间复杂度的操作.
4️⃣参数与返回值
参数 :args,构造新元素所需的参数(会被转发给元素的构造函数).
返回值 :无(none).
5️⃣常见使用场景当队列存储自定义类对象 时,
emplace可以避免临时对象的创建和拷贝,显著提升性能.需要直接用构造函数参数入队时,
emplace比push更简洁(无需显式创建对象).
13.6 pop介绍
这是C++ 标准库中std::priority_queue::pop成员函数的官方文档.
1️⃣函数签名void pop();
无参数,无返回值.
注意:
pop()仅删除队首(堆顶)元素,不会返回被删除的元素值 .如果需要获取元素值,必须先用top()读取,再调用pop()删除.
2️⃣核心功能这个函数用于删除优先队列的队首(堆顶)元素 ,也就是当前队列中优先级最高的元素(默认大顶堆中是最大的元素),队列的大小会减 1.被删除的元素会调用其析构函数释放资源.
3️⃣实现原理因为
std::priority_queue是容器适配器,pop()函数的底层执行两步操作:①维护堆结构 :调用
pop_heap算法,将堆顶元素移动到底层容器的末尾,并调整剩余元素使其重新满足堆的性质.②删除元素 :调用底层容器的
pop_back()(比如默认底层容器std::vector的pop_back()),删除容器末尾的元素(即原堆顶元素).时间复杂度为 O(log n) ,因为
pop_heap是对数时间复杂度的操作.
4️⃣参数与返回值
参数 :无(none)。
返回值 :无(none)。
5️⃣常见注意事项
空队列保护 :如果队列为空时调用pop(),会触发未定义行为(比如程序崩溃),因此必须先用empty()判断队列是否为空.
与top()配合 :因为pop()不返回值,所以通常会先调用top()获取堆顶元素的值,再调用pop()删除元素.
13.7 swap介绍
这是C++标准库中std::priority_queue::swap成员函数的官方文档.
1️⃣函数签名void swap (priority_queue& x) noexcept (/see below /);
参数priority_queue& x:另一个同类型的优先队列(模板参数T、底层容器Container、比较规则Compare必须完全一致),用于和当前队列交换内容.
noexcept说明 :该函数是否为noexcept取决于底层容器的swap操作和比较函数的swap操作是否为noexcept(例如默认底层容器vector的swap是noexcept,因此队列的swap通常也是noexcept).返回值:
void,无返回值.
2️⃣核心功能这个函数用于完整交换当前优先队列与另一个同类型队列的全部内容 ,包括:
底层容器的所有元素
内部的比较函数对象
交换后,两个队列的元素和比较规则会完全互换,且时间复杂度由底层容器的
swap决定(通常为 O(1) ,因为vector、deque等容器的swap仅交换内部指针).
3️⃣实现原理因为
std::priority_queue是容器适配器,swap()函数的底层会执行两步操作:①调用底层容器的非成员
swap函数,交换两个队列的底层容器.②调用比较函数的
swap函数,交换两个队列的比较规则.这保证了交换操作的高效性,无需拷贝或移动元素.
4️⃣参数与返回值
参数 :x,另一个同类型的优先队列(模板参数必须完全匹配),队列大小可以不同.
返回值 :无(none).
5️⃣常见使用场景需要快速交换两个优先队列的内容,比逐个元素拷贝高效得多(时间复杂度通常为 O(1)).
在算法中临时交换队列状态,或用于容器适配器的高效重置.
14. 关于priority_queue的一些oj题目
14.1数组中第K个大的元素
cpp
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 将数组中的元素先放入优先级队列中
priority_queue<int> p(nums.begin(), nums.end());
// 将优先级队列中前k-1个元素删除掉
for(int i= 0; i < k-1; ++i)
{
p.pop();
}
return p.top();
}
};
14.2前k个高频元素
cpp
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
// 1. 统计每个数字的出现频率
unordered_map<int, int> count;
for (int num : nums) {
count[num]++;
}
// 2. 小顶堆:按频率从小到大排序,堆顶是频率最小的元素
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
for (auto& [num, freq] : count) {
pq.push({freq, num});
if (pq.size() > k) {
pq.pop(); // 堆大小超过 K 时,弹出频率最小的元素
}
}
// 3. 收集结果
vector<int> res;
while (!pq.empty()) {
res.push_back(pq.top().second);
pq.pop();
}
return res;
}
};
14.3数据流中的第k大元素
cpp
class KthLargest {
private:
// 小顶堆:存储当前数据流中最大的K个元素,堆顶是第K大元素
priority_queue<int, vector<int>, greater<int>> min_heap;
int k_; // 保存K的值
public:
KthLargest(int k, vector<int>& nums) : k_(k) {
// 初始化:将所有初始元素加入堆,并维护堆的大小为K
for (int num : nums) {
min_heap.push(num);
if (min_heap.size() > k_) {
min_heap.pop(); // 弹出最小的元素,保证堆大小为K
}
}
}
int add(int val) {
// 若堆大小不足K,直接加入
if (min_heap.size() < k_) {
min_heap.push(val);
}
// 若新元素比堆顶大(说明它应该进入前K大),则替换堆顶
else if (val > min_heap.top()) {
min_heap.pop();
min_heap.push(val);
}
// 堆顶就是当前第K大元素
return min_heap.top();
}
};
14.4最接近原点的k个点
cpp
class Solution {
public:
vector<vector<int>> kClosest(vector<vector<int>>& points, int k) {
// 大顶堆:存储(距离平方,点坐标),按距离平方从大到小排序
priority_queue<pair<long long, vector<int>>> max_heap;
for (auto& point : points) {
// 计算距离平方(用long long避免溢出)
long long dist_sq = (long long)point[0] * point[0] + (long long)point[1] * point[1];
max_heap.push({dist_sq, point});
// 堆大小超过K时,弹出最远的点(堆顶)
if (max_heap.size() > k) {
max_heap.pop();
}
}
// 收集堆内的K个最近点
vector<vector<int>> res;
while (!max_heap.empty()) {
res.push_back(max_heap.top().second);
max_heap.pop();
}
return res;
}
};
15. priority_queue核心框架接口的模拟实现
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
// 模板参数:T-元素类型,Container-底层容器(默认vector),Compare-比较规则(默认less)
template <class T, class Container = vector<T>, class Compare = less<T>>
class PriorityQueue {
public:
// 默认构造:空队列
PriorityQueue() : _con(), _comp() {}
// 范围构造:迭代器范围初始化并构建堆
template <class InputIterator>
PriorityQueue(InputIterator first, InputIterator last) : _con(first, last), _comp() {
make_heap(_con.begin(), _con.end(), _comp);
}
// 判空:直接调用底层容器empty()
bool empty() const {
return _con.empty();
}
// 获取大小:直接调用底层容器size()
size_t size() const {
return _con.size();
}
// 访问堆顶(优先级最高元素)
const T& top() const {
if (empty()) {
cerr << "Error: priority_queue is empty!" << endl;
abort();
}
return _con.front();
}
// 入队:尾插 + 调整堆
void push(const T& val) {
_con.push_back(val);
push_heap(_con.begin(), _con.end(), _comp);
}
// C++11 右值引用版本(优化拷贝)
void push(T&& val) {
_con.push_back(move(val));
push_heap(_con.begin(), _con.end(), _comp);
}
// 出队:调整堆(堆顶移尾部) + 尾删
void pop() {
if (empty()) {
cerr << "Error: pop on empty priority_queue!" << endl;
abort();
}
pop_heap(_con.begin(), _con.end(), _comp);
_con.pop_back();
}
// 交换两个队列:交换底层容器+比较器
void swap(PriorityQueue& other) noexcept {
using std::swap;
swap(_con, other._con);
swap(_comp, other._comp);
}
private:
Container _con; // 底层容器(存储元素)
Compare _comp; // 优先级比较器
};
// 全局swap函数(适配标准库)
template <class T, class Container, class Compare>
void swap(PriorityQueue<T, Container, Compare>& pq1, PriorityQueue<T, Container, Compare>& pq2) noexcept {
pq1.swap(pq2);
}
int main() {
// 1. 大顶堆(默认less<int>,堆顶是最大元素)
PriorityQueue<int> pq1;
pq1.push(30);
pq1.push(10);
pq1.push(20);
cout << "大顶堆堆顶:" << pq1.top() << endl; // 输出 30
pq1.pop();
cout << "出队后堆顶:" << pq1.top() << endl; // 输出 20
// 2. 小顶堆(greater<int>,堆顶是最小元素)
PriorityQueue<int, vector<int>, greater<int>> pq2;
pq2.push(30);
pq2.push(10);
pq2.push(20);
cout << "小顶堆堆顶:" << pq2.top() << endl; // 输出 10
return 0;
}
16. 适配器介绍
适配器是一种设计模式 ,核心作用是接口转换 :它将一个已有类/对象的接口,转换成另一个符合业务需求的接口,让原本接口不兼容的组件可以协同工作.
在 C++ 标准库中,最常见的是容器适配器 (如
queue、stack、priority_queue),此外还有迭代器适配器(如reverse_iterator)、函数适配器(如bind)等.
1️⃣核心特点:以容器适配器为例C++ 中的容器适配器不直接管理存储 ,而是封装一个底层容器(如
vector、deque),并通过接口适配,对外提供更简洁、场景化的操作.
①核心逻辑:"封装 + 适配"
封装底层容器 :复用现有容器的存储能力(如deque的高效增删、vector的随机访问),避免重复实现存储逻辑.
适配接口 :对外提供符合特定场景的极简接口,隐藏底层容器的复杂细节.比如
queue(队列)是典型的容器适配器:底层默认封装
deque,但对外只暴露push(队尾入队)、pop(队首出队)、front(队首)、back(队尾)等 FIFO(先进先出)相关接口。底层
deque的push_back、pop_front等接口被适配成了队列的操作,上层代码无需关心底层是deque还是vector.
②关键特性
| 特性 | 说明 |
|---|---|
| 复用性 | 基于现有容器实现,避免重复造轮子(如 priority_queue 复用 vector 的存储 + 堆算法实现优先级) |
| 简洁性 | 对外接口极简,符合单一职责(如 stack 只暴露 push/pop/top,不关心底层是 deque 还是 vector) |
| 灵活性 | 可替换底层容器(如 queue 可指定用 vector 替代默认的 deque),或调整适配逻辑(如 priority_queue 换比较器实现大/小顶堆) |
| 解耦性 | 上层逻辑只依赖适配器的接口,不依赖底层容器,降低代码耦合度 |
2️⃣典型示例:
priority_queue作为适配器
priority_queue是基于堆的容器适配器 ,核心适配逻辑:①底层容器 :默认封装
vector(支持随机访问,适合堆算法).②堆算法适配 :通过
make_heap、push_heap、pop_heap等算法,将vector的接口适配成优先级队列的操作.③比较器适配 :通过自定义比较器(如
less<T>/greater<T>),实现大顶堆/小顶堆的切换.对外仅暴露
top(堆顶)、push(入队)、pop(出队)等优先级相关接口,底层的堆调整和容器操作完全被隐藏.
3️⃣适配器的价值适配器本质是"接口的中间层",它让代码更聚焦于业务场景(如队列、栈、优先队列),而不用关心底层实现细节,同时最大化复用现有代码,提升开发效率和可维护性.
17. deque的简单介绍(了解)
虽然stack和queue中也可以存放元素,但在STL中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为stack和队列只是对其他容器的接口进行了包装,STL中stack和queue默认使用deque.
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与list比较,空间利用率比较高.是vector和list的一种平衡方案.
deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:
双端队列底层是一段假象的连续空间,实际是分段连续的,为了维护其"整体连续"以及随机访问的假象,落在了deque的迭代器身上,因此deque的迭代器设计就比较复杂,如下图所示:
那deque是如何借助其迭代器维护其假想连续的结构呢?
底层实现原理
deque的底层结构分为两部分:①中控数组(map) :一个动态数组,每个元素是指向缓冲区的指针.
②缓冲区(buffer) :多个固定大小的连续内存块,用于存储实际元素.
随机访问 :访问deque[i]时,先通过中控数组找到对应的缓冲区,再在缓冲区内偏移找到元素,需两次指针跳转(时间复杂度仍为 O(1),但比vector略慢).
双端插入 :队首/队尾插入时,若对应缓冲区未满则直接插入;若已满则新增缓冲区并更新中控数组,无需搬迁现有元素.
适用场景
deque适合需要双端高效增删 + 偶尔随机访问 的场景:①队列/栈的底层容器 :C++ 标准库的
queue和stack默认使用deque作为底层容器(因为双端操作高效,且无需随机访问时也能保证性能).②滑动窗口问题 :需要在窗口两端添加/删除元素,并随机访问窗口内的元素.
③频繁双端操作的场景 :比如任务队列、事件队列,需要快速在头部或尾部添加/取出任务.
dequevsvectorvslist对比
| 特性 | deque |
vector |
list |
|---|---|---|---|
| 内存连续性 | 非连续(分段连续) | 连续 | 非连续(节点式) |
| 随机访问 | 支持(O(1),略慢) | 支持(O(1),最快) | 不支持 |
| 队首增删 | O(1) | O(n) | O(1) |
| 队尾增删 | O(1) | O(1)(扩容时 O(n)) | O(1) |
| 中间增删 | O(n) | O(n) | O(1) |
| 内存分配效率 | 高(新增缓冲区,无搬迁) | 低(扩容时搬迁数据) | 高(节点分配,无搬迁) |
| 迭代器稳定性 | 插入/删除两端时迭代器有效,中间操作可能失效 | 插入/删除中间时迭代器失效 | 插入/删除时迭代器始终有效 |
deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构.
17.1 为什么选择deque作为stack和queue的底层默认容器?
stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list.但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:
①stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作.
②在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高.结合了deque的优点,而完美的避开了其缺陷.
敬请期待下一篇文章内容-->C++拓展学习之反向迭代器实现、计算器实现以及逆波兰表达式!














































