C++ STL 之 stack、queue 与 priority_queue 详解:用法速查 → 底层原理 → 面试题

C++ STL 之 stack、queue 与 priority_queue 详解:用法速查 → 底层原理 → 面试题

本文面向面试和日常开发,三段式结构:先讲怎么用,再讲底层怎么实现,最后给口语化面试答案。这三个容器不是独立的数据结构,而是容器适配器------对底层容器的接口做了一层封装,只暴露特定的操作语义。


一、用法速查

1.1 stack ------ 后进先出

cpp 复制代码
#include <stack>
stack<int> st;

st.push(1);             // 入栈
st.push(2);
st.push(3);
cout << st.top();       // 3,获取栈顶元素(不删除)
st.pop();               // 弹出栈顶,无返回值
cout << st.top();       // 2

cout << st.size();      // 2
cout << st.empty();     // 0 (false)

无迭代器,不支持遍历,只能通过 top 访问栈顶。

1.2 queue ------ 先进先出

cpp 复制代码
#include <queue>
queue<int> q;

q.push(1);              // 队尾入队
q.push(2);
q.push(3);
cout << q.front();      // 1,队头(最早入队的元素)
cout << q.back();       // 3,队尾
q.pop();                // 弹出队头,无返回值
cout << q.front();      // 2

cout << q.size();       // 2
cout << q.empty();      // 0

1.3 priority_queue ------ 优先级队列

默认是大顶堆(最大元素在堆顶)。

cpp 复制代码
#include <queue>
priority_queue<int> pq;

pq.push(3);
pq.push(1);
pq.push(2);
cout << pq.top();       // 3,最大元素
pq.pop();               // 弹出堆顶
cout << pq.top();       // 2

// C++11 emplace 原地构造
pq.emplace(5);

priority_queue 没有 front/back,只有 top

1.4 自定义底层容器

三个容器适配器都接受第二个模板参数指定底层容器:

cpp 复制代码
// stack 用 vector 做底层
stack<int, vector<int>> st;

// queue 用 list 做底层(vector 没有 pop_front)
queue<int, list<int>> q;

// priority_queue 默认用 vector,也可指定 deque
priority_queue<int, deque<int>> pq;

// 小顶堆
priority_queue<int, vector<int>, greater<int>> minHeap;

1.5 priority_queue 自定义比较

默认大顶堆(less<int> 表现为最大元素在堆顶)。小顶堆的三种写法:

cpp 复制代码
// 方式1:greater 仿函数
priority_queue<int, vector<int>, greater<int>> minHeap;

// 方式2:lambda(C++11)
auto cmp = [](int a, int b) { return a > b; };
priority_queue<int, vector<int>, decltype(cmp)> minHeap(cmp);

// 方式3:自定义仿函数
struct MyCmp {
    bool operator()(int a, int b) const { return a > b; }
};
priority_queue<int, vector<int>, MyCmp> minHeap;

注意:比较器语义是 a > b 返回 true 时 a 比 b 优先级低(小顶堆)。默认 less<T> 反而让最大元素在堆顶------因为底层堆算法用 Compare 不成立时交换。

1.6 自定义类型的 priority_queue

cpp 复制代码
struct Task {
    int priority;
    string name;

    // 方式A:重载 operator<(默认大顶堆用这个)
    bool operator<(const Task &t) const {
        return priority < t.priority;
    }
};
priority_queue<Task> pq;  // priority 大的先出

// 方式B:传仿函数,不改结构体
struct TaskCmp {
    bool operator()(const Task &a, const Task &b) const {
        return a.priority < b.priority;
    }
};
priority_queue<Task, vector<Task>, TaskCmp> pq;

1.7 完整示例

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

struct Task {
    int priority;
    string name;
    Task(int p, string n) : priority(p), name(n) {}
    bool operator<(const Task &t) const {
        return priority < t.priority;
    }
};

int main() {
    // stack
    stack<int> st;
    st.push(10); st.push(20); st.push(30);
    while (!st.empty()) {
        cout << st.top() << " "; st.pop();
    }

    // queue
    queue<int> q;
    q.push(10); q.push(20); q.push(30);
    while (!q.empty()) {
        cout << q.front() << " "; q.pop();
    }

    // priority_queue(大顶堆)
    priority_queue<Task> tasks;
    tasks.emplace(3, "low");
    tasks.emplace(5, "high");
    tasks.emplace(4, "mid");
    while (!tasks.empty()) {
        cout << tasks.top().name << " "; tasks.pop();
    }
}

二、底层原理

2.1 容器适配器设计模式

stack、queue、priority_queue 不是独立的数据结构,标准库称它们为容器适配器------对底层容器的接口做一层包装,只暴露特定操作,隐藏其他能力。
#mermaid-svg-XlMR2y3uou6ataVS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XlMR2y3uou6ataVS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XlMR2y3uou6ataVS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XlMR2y3uou6ataVS .error-icon{fill:#552222;}#mermaid-svg-XlMR2y3uou6ataVS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XlMR2y3uou6ataVS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XlMR2y3uou6ataVS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XlMR2y3uou6ataVS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XlMR2y3uou6ataVS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XlMR2y3uou6ataVS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XlMR2y3uou6ataVS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XlMR2y3uou6ataVS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XlMR2y3uou6ataVS .marker.cross{stroke:#333333;}#mermaid-svg-XlMR2y3uou6ataVS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XlMR2y3uou6ataVS p{margin:0;}#mermaid-svg-XlMR2y3uou6ataVS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XlMR2y3uou6ataVS .cluster-label text{fill:#333;}#mermaid-svg-XlMR2y3uou6ataVS .cluster-label span{color:#333;}#mermaid-svg-XlMR2y3uou6ataVS .cluster-label span p{background-color:transparent;}#mermaid-svg-XlMR2y3uou6ataVS .label text,#mermaid-svg-XlMR2y3uou6ataVS span{fill:#333;color:#333;}#mermaid-svg-XlMR2y3uou6ataVS .node rect,#mermaid-svg-XlMR2y3uou6ataVS .node circle,#mermaid-svg-XlMR2y3uou6ataVS .node ellipse,#mermaid-svg-XlMR2y3uou6ataVS .node polygon,#mermaid-svg-XlMR2y3uou6ataVS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XlMR2y3uou6ataVS .rough-node .label text,#mermaid-svg-XlMR2y3uou6ataVS .node .label text,#mermaid-svg-XlMR2y3uou6ataVS .image-shape .label,#mermaid-svg-XlMR2y3uou6ataVS .icon-shape .label{text-anchor:middle;}#mermaid-svg-XlMR2y3uou6ataVS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XlMR2y3uou6ataVS .rough-node .label,#mermaid-svg-XlMR2y3uou6ataVS .node .label,#mermaid-svg-XlMR2y3uou6ataVS .image-shape .label,#mermaid-svg-XlMR2y3uou6ataVS .icon-shape .label{text-align:center;}#mermaid-svg-XlMR2y3uou6ataVS .node.clickable{cursor:pointer;}#mermaid-svg-XlMR2y3uou6ataVS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XlMR2y3uou6ataVS .arrowheadPath{fill:#333333;}#mermaid-svg-XlMR2y3uou6ataVS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XlMR2y3uou6ataVS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XlMR2y3uou6ataVS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XlMR2y3uou6ataVS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XlMR2y3uou6ataVS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XlMR2y3uou6ataVS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XlMR2y3uou6ataVS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XlMR2y3uou6ataVS .cluster text{fill:#333;}#mermaid-svg-XlMR2y3uou6ataVS .cluster span{color:#333;}#mermaid-svg-XlMR2y3uou6ataVS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XlMR2y3uou6ataVS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XlMR2y3uou6ataVS rect.text{fill:none;stroke-width:0;}#mermaid-svg-XlMR2y3uou6ataVS .icon-shape,#mermaid-svg-XlMR2y3uou6ataVS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XlMR2y3uou6ataVS .icon-shape p,#mermaid-svg-XlMR2y3uou6ataVS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XlMR2y3uou6ataVS .icon-shape .label rect,#mermaid-svg-XlMR2y3uou6ataVS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XlMR2y3uou6ataVS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XlMR2y3uou6ataVS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XlMR2y3uou6ataVS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户代码
stack 接口

push/top/pop
queue 接口

push/front/pop
priority_queue 接口

push/top/pop
底层容器 deque
底层容器 vector

设计意图:使用者不需要知道底层是数组还是链表,只需理解栈是 LIFO、队列是 FIFO。底层的交换、扩容、碎片管理都由容器自身处理。

2.2 为什么默认用 deque 而不是 vector?

高频面试题。stack 和 queue 的默认底层是 deque,priority_queue 是 vector

容器 push_back push_front pop_front 随机访问 扩容搬迁
vector O(1) 均摊 O(n) O(n) O(1) 可能触发
deque O(1) 均摊 O(1) 均摊 O(1) O(1) 不触发
list O(1) O(1) O(1) 不支持 不触发

stack 用 deque 而非 vector:vector 也能做栈(push_back/pop_back/back 都支持),但 deque 的两端 O(1) 操作更灵活,且 deque 是分段连续存储,插入时不触发整体搬迁,空间浪费更小。

queue 不用 vector :vector 没有 pop_front(头部删除 O(n)),而 queue 必须支持头部删除。deque 的 pop_front 是 O(1)。

priority_queue 默认用 vector :堆算法需要随机访问迭代器(make_heap/push_heap/pop_heap 都依赖 [first, last)),vector 比 deque 更紧凑、缓存命中率更高。

2.3 priority_queue 的堆算法

priority_queue 底层不是显式的树,而是隐式二叉堆 ,存储在连续数组中。v[0] 是堆顶,v[i] 的左子是 v[2i+1],右子是 v[2i+2]
#mermaid-svg-OhroFcAE4aDf9GVF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-OhroFcAE4aDf9GVF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-OhroFcAE4aDf9GVF .error-icon{fill:#552222;}#mermaid-svg-OhroFcAE4aDf9GVF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-OhroFcAE4aDf9GVF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-OhroFcAE4aDf9GVF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-OhroFcAE4aDf9GVF .marker.cross{stroke:#333333;}#mermaid-svg-OhroFcAE4aDf9GVF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-OhroFcAE4aDf9GVF p{margin:0;}#mermaid-svg-OhroFcAE4aDf9GVF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-OhroFcAE4aDf9GVF .cluster-label text{fill:#333;}#mermaid-svg-OhroFcAE4aDf9GVF .cluster-label span{color:#333;}#mermaid-svg-OhroFcAE4aDf9GVF .cluster-label span p{background-color:transparent;}#mermaid-svg-OhroFcAE4aDf9GVF .label text,#mermaid-svg-OhroFcAE4aDf9GVF span{fill:#333;color:#333;}#mermaid-svg-OhroFcAE4aDf9GVF .node rect,#mermaid-svg-OhroFcAE4aDf9GVF .node circle,#mermaid-svg-OhroFcAE4aDf9GVF .node ellipse,#mermaid-svg-OhroFcAE4aDf9GVF .node polygon,#mermaid-svg-OhroFcAE4aDf9GVF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-OhroFcAE4aDf9GVF .rough-node .label text,#mermaid-svg-OhroFcAE4aDf9GVF .node .label text,#mermaid-svg-OhroFcAE4aDf9GVF .image-shape .label,#mermaid-svg-OhroFcAE4aDf9GVF .icon-shape .label{text-anchor:middle;}#mermaid-svg-OhroFcAE4aDf9GVF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-OhroFcAE4aDf9GVF .rough-node .label,#mermaid-svg-OhroFcAE4aDf9GVF .node .label,#mermaid-svg-OhroFcAE4aDf9GVF .image-shape .label,#mermaid-svg-OhroFcAE4aDf9GVF .icon-shape .label{text-align:center;}#mermaid-svg-OhroFcAE4aDf9GVF .node.clickable{cursor:pointer;}#mermaid-svg-OhroFcAE4aDf9GVF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-OhroFcAE4aDf9GVF .arrowheadPath{fill:#333333;}#mermaid-svg-OhroFcAE4aDf9GVF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-OhroFcAE4aDf9GVF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-OhroFcAE4aDf9GVF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OhroFcAE4aDf9GVF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-OhroFcAE4aDf9GVF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OhroFcAE4aDf9GVF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-OhroFcAE4aDf9GVF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-OhroFcAE4aDf9GVF .cluster text{fill:#333;}#mermaid-svg-OhroFcAE4aDf9GVF .cluster span{color:#333;}#mermaid-svg-OhroFcAE4aDf9GVF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-OhroFcAE4aDf9GVF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-OhroFcAE4aDf9GVF rect.text{fill:none;stroke-width:0;}#mermaid-svg-OhroFcAE4aDf9GVF .icon-shape,#mermaid-svg-OhroFcAE4aDf9GVF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-OhroFcAE4aDf9GVF .icon-shape p,#mermaid-svg-OhroFcAE4aDf9GVF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-OhroFcAE4aDf9GVF .icon-shape .label rect,#mermaid-svg-OhroFcAE4aDf9GVF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-OhroFcAE4aDf9GVF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-OhroFcAE4aDf9GVF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-OhroFcAE4aDf9GVF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 堆顶 50
左子 30
右子 40
10
20
35
38

数组存储:[50, 30, 40, 10, 20, 35, 38],索引 0 到 6。

建立堆 make_heap --- O(n)

cpp 复制代码
vector<int> v{3, 1, 4, 1, 5, 9, 2, 6};
make_heap(v.begin(), v.end());
// 结果示例: {9, 6, 4, 3, 5, 1, 2, 1}

从最后一个非叶子节点开始向下调整(下滤),整体复杂度严格 O(n)------底层节点多但下滤步数少,顶层节点少但下滤步数多,总和收敛到 O(n),而非 O(n log n)。

插入 push_heap --- O(log n)

cpp 复制代码
v.push_back(8);
push_heap(v.begin(), v.end());

#mermaid-svg-ShnSs0btQAfu27BV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ShnSs0btQAfu27BV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ShnSs0btQAfu27BV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ShnSs0btQAfu27BV .error-icon{fill:#552222;}#mermaid-svg-ShnSs0btQAfu27BV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ShnSs0btQAfu27BV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ShnSs0btQAfu27BV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ShnSs0btQAfu27BV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ShnSs0btQAfu27BV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ShnSs0btQAfu27BV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ShnSs0btQAfu27BV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ShnSs0btQAfu27BV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ShnSs0btQAfu27BV .marker.cross{stroke:#333333;}#mermaid-svg-ShnSs0btQAfu27BV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ShnSs0btQAfu27BV p{margin:0;}#mermaid-svg-ShnSs0btQAfu27BV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ShnSs0btQAfu27BV .cluster-label text{fill:#333;}#mermaid-svg-ShnSs0btQAfu27BV .cluster-label span{color:#333;}#mermaid-svg-ShnSs0btQAfu27BV .cluster-label span p{background-color:transparent;}#mermaid-svg-ShnSs0btQAfu27BV .label text,#mermaid-svg-ShnSs0btQAfu27BV span{fill:#333;color:#333;}#mermaid-svg-ShnSs0btQAfu27BV .node rect,#mermaid-svg-ShnSs0btQAfu27BV .node circle,#mermaid-svg-ShnSs0btQAfu27BV .node ellipse,#mermaid-svg-ShnSs0btQAfu27BV .node polygon,#mermaid-svg-ShnSs0btQAfu27BV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ShnSs0btQAfu27BV .rough-node .label text,#mermaid-svg-ShnSs0btQAfu27BV .node .label text,#mermaid-svg-ShnSs0btQAfu27BV .image-shape .label,#mermaid-svg-ShnSs0btQAfu27BV .icon-shape .label{text-anchor:middle;}#mermaid-svg-ShnSs0btQAfu27BV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ShnSs0btQAfu27BV .rough-node .label,#mermaid-svg-ShnSs0btQAfu27BV .node .label,#mermaid-svg-ShnSs0btQAfu27BV .image-shape .label,#mermaid-svg-ShnSs0btQAfu27BV .icon-shape .label{text-align:center;}#mermaid-svg-ShnSs0btQAfu27BV .node.clickable{cursor:pointer;}#mermaid-svg-ShnSs0btQAfu27BV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ShnSs0btQAfu27BV .arrowheadPath{fill:#333333;}#mermaid-svg-ShnSs0btQAfu27BV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ShnSs0btQAfu27BV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ShnSs0btQAfu27BV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ShnSs0btQAfu27BV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ShnSs0btQAfu27BV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ShnSs0btQAfu27BV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ShnSs0btQAfu27BV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ShnSs0btQAfu27BV .cluster text{fill:#333;}#mermaid-svg-ShnSs0btQAfu27BV .cluster span{color:#333;}#mermaid-svg-ShnSs0btQAfu27BV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ShnSs0btQAfu27BV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ShnSs0btQAfu27BV rect.text{fill:none;stroke-width:0;}#mermaid-svg-ShnSs0btQAfu27BV .icon-shape,#mermaid-svg-ShnSs0btQAfu27BV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ShnSs0btQAfu27BV .icon-shape p,#mermaid-svg-ShnSs0btQAfu27BV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ShnSs0btQAfu27BV .icon-shape .label rect,#mermaid-svg-ShnSs0btQAfu27BV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ShnSs0btQAfu27BV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ShnSs0btQAfu27BV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ShnSs0btQAfu27BV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大于父节点
小于等于父节点
新元素追加到末尾
与父节点比较
交换父子位置
上滤结束

步骤:新元素 push_back 到末尾(成为最后一个叶子)→ 与父节点比较 → 大于父节点则交换 → 重复直到根或满足堆性质。

弹出 pop_heap --- O(log n)

cpp 复制代码
pop_heap(v.begin(), v.end());
int maxVal = v.back();
v.pop_back();

#mermaid-svg-AM3YifIz0WKHqMdG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AM3YifIz0WKHqMdG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AM3YifIz0WKHqMdG .error-icon{fill:#552222;}#mermaid-svg-AM3YifIz0WKHqMdG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AM3YifIz0WKHqMdG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AM3YifIz0WKHqMdG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AM3YifIz0WKHqMdG .marker.cross{stroke:#333333;}#mermaid-svg-AM3YifIz0WKHqMdG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AM3YifIz0WKHqMdG p{margin:0;}#mermaid-svg-AM3YifIz0WKHqMdG .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AM3YifIz0WKHqMdG .cluster-label text{fill:#333;}#mermaid-svg-AM3YifIz0WKHqMdG .cluster-label span{color:#333;}#mermaid-svg-AM3YifIz0WKHqMdG .cluster-label span p{background-color:transparent;}#mermaid-svg-AM3YifIz0WKHqMdG .label text,#mermaid-svg-AM3YifIz0WKHqMdG span{fill:#333;color:#333;}#mermaid-svg-AM3YifIz0WKHqMdG .node rect,#mermaid-svg-AM3YifIz0WKHqMdG .node circle,#mermaid-svg-AM3YifIz0WKHqMdG .node ellipse,#mermaid-svg-AM3YifIz0WKHqMdG .node polygon,#mermaid-svg-AM3YifIz0WKHqMdG .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AM3YifIz0WKHqMdG .rough-node .label text,#mermaid-svg-AM3YifIz0WKHqMdG .node .label text,#mermaid-svg-AM3YifIz0WKHqMdG .image-shape .label,#mermaid-svg-AM3YifIz0WKHqMdG .icon-shape .label{text-anchor:middle;}#mermaid-svg-AM3YifIz0WKHqMdG .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AM3YifIz0WKHqMdG .rough-node .label,#mermaid-svg-AM3YifIz0WKHqMdG .node .label,#mermaid-svg-AM3YifIz0WKHqMdG .image-shape .label,#mermaid-svg-AM3YifIz0WKHqMdG .icon-shape .label{text-align:center;}#mermaid-svg-AM3YifIz0WKHqMdG .node.clickable{cursor:pointer;}#mermaid-svg-AM3YifIz0WKHqMdG .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AM3YifIz0WKHqMdG .arrowheadPath{fill:#333333;}#mermaid-svg-AM3YifIz0WKHqMdG .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AM3YifIz0WKHqMdG .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AM3YifIz0WKHqMdG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AM3YifIz0WKHqMdG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AM3YifIz0WKHqMdG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AM3YifIz0WKHqMdG .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AM3YifIz0WKHqMdG .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AM3YifIz0WKHqMdG .cluster text{fill:#333;}#mermaid-svg-AM3YifIz0WKHqMdG .cluster span{color:#333;}#mermaid-svg-AM3YifIz0WKHqMdG div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AM3YifIz0WKHqMdG .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AM3YifIz0WKHqMdG rect.text{fill:none;stroke-width:0;}#mermaid-svg-AM3YifIz0WKHqMdG .icon-shape,#mermaid-svg-AM3YifIz0WKHqMdG .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AM3YifIz0WKHqMdG .icon-shape p,#mermaid-svg-AM3YifIz0WKHqMdG .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AM3YifIz0WKHqMdG .icon-shape .label rect,#mermaid-svg-AM3YifIz0WKHqMdG .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AM3YifIz0WKHqMdG .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AM3YifIz0WKHqMdG .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AM3YifIz0WKHqMdG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 小于子节点
大于等于子节点
堆顶与末尾元素交换
新堆顶开始下滤
与较大子节点比较
与较大子节点交换
下滤结束

步骤:堆顶与末尾元素交换 → 新的 v.back() 是最大值 → 新堆顶下滤恢复堆性质 → 用户取 v.back()pop_back()。这就是 priority_queue 没有 front()/back() 的原因------弹出是 pop_heap + pop_back 两步完成。

2.4 emplace 与 push 的区别

cpp 复制代码
priority_queue<pair<int, string>> pq;

pq.push(make_pair(1, "a"));   // 构造 pair → 拷贝/移动进堆
pq.emplace(2, "b");           // 在堆内原地构造,省一次拷贝

emplace 接受构造参数的完美转发,在容器内部原地构造元素。push 先在外面构造好再搬进去。对于平凡类型无区别,对自定义类型能省一次移动构造。

2.5 C++20 range 适配器的影响

C++20 的 range 库没有改变 priority_queue 的实现,但提供了新的操作方式:

cpp 复制代码
#include <ranges>
#include <vector>

vector<int> v{3, 1, 4, 1, 5, 9, 2, 6};

// 取前 3 大的元素(无序),等价于小顶堆连续 pop
ranges::nth_element(v, v.begin() + 3, greater<int>());

// 构造 priority_queue 的 range 写法
priority_queue<int> pq(v.begin(), v.end());

某些本需要用 priority_queue 的场景有了替代方案。例如"从 100 万个数里找最大的 100 个":

cpp 复制代码
// 方法 A:priority_queue
priority_queue<int> pq(v.begin(), v.end());
for (int i = 0; i < 100; ++i) { result.push_back(pq.top()); pq.pop(); }

// 方法 B:ranges::nth_element(C++20)
ranges::nth_element(v, v.begin() + 100, greater<int>());
// v[0..99] 是最大的 100 个元素(不保证有序)

ranges::nth_element 原地分区、缓存友好,但 priority_queue 可以增量处理(数据不是一次性准备好的),这是它不可替代的地方。


三、面试题 + 口语化答案

Q1:stack 为什么不提供迭代器?

"栈的设计原则是 LIFO------你只能操作栈顶。如果提供迭代器,外部代码就能遍历所有元素、随意修改中间数据,破坏了栈的语义约束。STL 用类型约束行为:stack 不提供 begin/end 就是为了保证你不能从中间偷看或修改。不够用就别用 stack,换 deque 或 vector。"

Q2:stack 和 queue 默认底层容器为什么是 deque?

"deque 的两端操作都是 O(1),内存分配不会像 vector 那样频繁搬迁。stack 用 vector 也能跑,但 deque 在空间和时间上更均衡。queue 不能用 vector------vector 没有 pop_front。deque 的 front/pop_front 都是 O(1),所以是 queue 的默认选择。"

Q3:priority_queue 默认是大顶堆还是小顶堆?怎么改?

"默认大顶堆------最大元素在 top()。改小顶堆:priority_queue<int, vector<int>, greater<int>>。这里 greater 做比较时 a > b 返回 true,对应小顶堆。lambda 写法需要 decltype 提取类型再加构造传参。关键:比较器返回 true 表示传入的 a 比 b 优先级低。"

Q4:emplace 和 push 有什么区别?

"emplace 是 C++11 加入的,参数是构造函数的参数列表,在容器内部原地构造对象,省一次拷贝/移动。push 先在外面构造好再拷进去。对于 int 这种平凡类型没区别,对于 Task(priority, name) 这种自定义类型能减少一次移动构造。"

Q5:自定义类型的 priority_queue 需要什么?

"两种方式:(1)类型内部重载 operator<,默认 less 会调用它。(2)不改类型本身,传第三个模板参数------仿函数或 lambda。注意比较器返回 true 表示 a 比 b 优先级低。记住 less 做的是大顶堆就行,很多人在这里写反。"

Q6:priority_queue 的 top() 为什么返回 const 引用?

"因为修改堆顶元素会破坏堆结构。拿到 mutable 引用后改掉值,堆不知道这个变化,不会重新调整。vector 做堆时可以通过 v0 修改------但那很危险,需要手动重新 make_heap。priority_queue 直接用 const 封死,从 API 层面保证堆的不变式。"

Q7:插入和弹出分别做了什么?

"插入 push():元素先 push_back 到末尾,然后 push_heap 做上滤,O(log n)。弹出 pop():调用 pop_heap(堆顶和末尾交换再下滤,O(log n)),再 pop_back 移除旧堆顶。用 vector 手动管理堆就是自己做 make_heap/push_heap/pop_heap 三步,priority_queue 帮你封装好了。"

Q8:用 priority_queue 实现 TopK 有几种思路?

"两种。思路 A:大顶堆,所有元素入堆再 pop K 次,O(n + k log n),空间 O(n)。思路 B:小顶堆,只维护 K 个元素------先入 K 个,后面每个和堆顶比,更大就 pop 再 push,O(n log k),空间 O(k)。数据量大时思路 B 更优。C++20 也可以用 ranges::nth_element 替代。"


一句话总结:stack、queue、priority_queue 不是独立容器,而是容器适配器------面试核心在于理解适配器设计模式、理解 deque 是最佳默认底层、理解 priority_queue 的隐式堆算法(上滤/下滤、不变式维护)如何工作。