C++ std::stack,queue和priority_queue的超详细指南

目录

一.stack(栈)

一).stack基本概念

1. 容器特性

二).stack的函数接口

1.push()

2.pop()

3.top()

4.size() 

5.empty()

三). stack模拟实现

四). 不同底层容器的性能对比 

二.queue(队列)

一).queue基本概念

1. 容器特性

二).queue的函数接口

三). queue的模拟实现

四). 不同底层容器的性能对比 

三.priority_queue(优先级队列 / 堆)

一).核心概念

1. 容器特性

二).priority_queue 的函数接口

三).priority_queue 模拟实现

四).仿函数 

四.deque(双端队列)(了解)

一).核心概念

1. 容器特性

2. 时间复杂度对比

 二).deque的函数接口

三).实现原理 

四).为什么选择deque作为stack和queue的底层默认容器


一.stack(栈)


一).stack基本概念

1. 容器特性

  • 后进先出(LIFO):最后压入的元素最先弹出

  • 适配器容器:基于其他序列容器实现(默认使用deque)

  • 头文件#include <stack>

  • 不支持

    • 随机访问

    • 迭代器遍历

    • 直接访问中间元素

stack<int> s1;                  // 默认使用deque
stack<int, vector<int>> s2;     // 使用vector作为底层
stack<int, list<int>> s3;       // 使用list作为底层

二).stack的函数接口


1.push()

将元素val压入stack中

int main()
{
    std::stack<int> mystack;
    for (int i=0; i<5; ++i){ 
        mystack.push(i);    //0 1 2 3 4
    }
}

2.pop()

将stack中尾部的元素弹出

int main()
{
    std::stack<int> mystack;
    for (int i = 0; i < 5; ++i) {
        mystack.push(i);
    }
    mystack.pop();  //4
}

3.top()

返回栈顶元素的引用

int main()
{
    std::stack<int> mystack;
    for (int i = 0; i < 5; ++i) {
        mystack.push(i);
    }
    int a=mystack.top(); //4
}

4.size() 

返回stack中元素的个数

int main()
{
    std::stack<int> mystack;
    for (int i = 0; i < 5; ++i) {
        mystack.push(i);
    }
    int a=mystack.size(); //5
}

5.empty()

检测stack是否为空

int main()
{
    std::stack<int> mystack;
    for (int i = 0; i < 5; ++i) {
        mystack.push(i);
    }
    bool i=mystack.empty();  //false
}
stack<string> history;

// 添加浏览记录
history.push("homepage");
history.emplace("product_detail");  // 直接构造

// 回退操作
while(history.size() > 1) {
    cout << "返回:" << history.top() << endl;
    history.pop();  // 必须单独调用pop
}

// 安全访问
if(!history.empty()) {
    history.top() = "modified_url";  // 可以修改栈顶元素
}

三). stack模拟实现

#pragma once
#include <vector>
#include <list>
#include <deque>

using namespace std;

namespace bit
{
	// Container 适配转换出 stack
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		const T& top() const
		{
			return _con.back();
		}
		size_t size() const
		{
			return _con.size();
		}
		bool empty() const
		{
			return _con.empty();
		}

	private:
		Container _con;
	};
}

四). 不同底层容器的性能对比 

操作deque实现vector实现list实现
push()O(1)平摊O(1)O(1)
pop()O(1)O(1)O(1)
top()O(1)O(1)O(1)
内存碎片
扩容代价

二.queue(队列)


一).queue基本概念

1. 容器特性

  • 先进先出(FIFO):最早入队的元素最先出队

  • 适配器容器:基于其他序列容器实现(默认使用deque

  • 头文件#include <queue>

  • 不支持

    • 随机访问

    • 迭代器遍历

    • 直接访问中间元素

二).queue的函数接口

大部分和stack用法一样,就不多说了

方法说明
push(value)元素入队(尾部)
emplace(args)直接构造元素(C++11)
pop()移除队首元素(无返回值)
front()访问队首元素(不删除)
back()访问队尾元素(不删除)
empty()判断队列是否为空
size()返回元素数量
queue<string> task_queue;

// 添加任务
task_queue.push("process_data");
task_queue.emplace("generate_report");  // 直接构造

// 处理任务队列
while(!task_queue.empty()) {
    cout << "处理任务:" << task_queue.front() << endl;
    task_queue.pop();  // 必须单独调用pop
}

// 安全访问
if(!task_queue.empty()) {
    task_queue.front() = "urgent_" + task_queue.front();
}

三). queue的模拟实现

#include <deque>

using namespace std;

namespace bit
{
	// Container 适配转换出 queue
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_front();
		}

		const T& front() const
		{
			return _con.front();
		}

		const T& back() const
		{
			return _con.back();
		}

		size_t size() const
		{
			return _con.size();
		}

		bool empty() const
		{
			return _con.empty();
		}

	private:
		Container _con;
	};
}

四). 不同底层容器的性能对比 

操作deque实现list实现vector实现(需支持pop_front)
push()O(1)O(1)O(1)
pop()O(1)O(1)O(n)
front()O(1)O(1)O(1)
内存碎片
适用场景通用队列大对象不推荐

三.priority_queue(优先级队列 / 堆)


一).核心概念

1. 容器特性

  • 优先级队列:元素按优先级顺序出队(默认最大优先)

  • 适配器容器:基于其他序列容器实现(默认使用vector

  • 头文件#include <queue>

  • 底层结构:使用堆算法(默认大顶堆)

  • 时间复杂度

    • 插入元素(push):O(log n)

    • 删除顶部元素(pop):O(log n)

    • 访问顶部元素(top):O(1)

priority_queue<int> pq1;                // 默认vector
priority_queue<int, deque<int>> pq2;    // 使用deque
priority_queue<int, vector<int>, greater<int>> pq3; // 小顶堆

支持容器必须满足随机访问迭代器(因此不能用list) 

二).priority_queue 的函数接口

大部分和stack用法一样,就不多说了

方法说明时间复杂度
push(value)插入元素O(log n)
emplace(args)构造并插入元素(C++11)O(log n)
pop()移除顶部元素O(log n)
top()访问顶部元素(不删除)O(1)
empty()判断队列是否为空O(1)
size()返回元素数量O(1)
// 默认大顶堆
priority_queue<int> max_heap;
max_heap.push(3);
max_heap.push(1);
max_heap.push(4);
cout << max_heap.top(); // 4

// 小顶堆
priority_queue<int, vector<int>, greater<int>> min_heap;
min_heap.push(3);
min_heap.push(1);
min_heap.push(4);
cout << min_heap.top(); // 1

三).priority_queue 模拟实现

Less构建大顶堆:当父节点 < 子节点时交换(升序比较器产生降序排列)

Greater构建小顶堆:当父节点 > 子节点时交换(降序比较器产生升序排列)

#pragma once
#include <vector>

using namespace std;  

// 仿函数:用于比较两个元素的'小于'关系
template<class T>
class Less {
public:
    // 重载函数调用运算符,实现元素比较
    bool operator()(const T& x, const T& y) {
        return x < y;  // 返回true表示x应该排在y前面(构建大顶堆)
    }
};

// 仿函数:用于比较两个元素的'大于'关系
template<class T>
class Greater {
public:
    // 重载函数调用运算符,实现元素比较
    bool operator()(const T& x, const T& y) {
        return x > y;  // 返回true表示x应该排在y前面(构建小顶堆)
    }
};

namespace bit { 
    template<class T, class Container = vector<T>, class Compare = Less<T>>
    class priority_queue {
    public:
        // 向上调整(堆化)操作,维护堆结构
        void AdjustUp(int child) {
            Compare com;  // 实例化比较器
            int parent = (child - 1) / 2;  // 计算父节点位置

            // 循环直到到达根节点或满足堆条件
            while (child > 0) {
                // 关键比较:判断父节点是否需要下沉
                if (com(_con[parent], _con[child])) {
                    // 交换父子节点值
                    swap(_con[parent], _con[child]);
                    // 更新节点位置
                    child = parent;
                    parent = (child - 1) / 2;
                }
                else {
                    break;  // 堆条件已满足,退出循环
                }
            }
        }

        // 插入元素操作
        void push(const T& x) {
            _con.push_back(x);          // 在容器尾部添加新元素
            AdjustUp(_con.size() - 1);  // 从最后位置开始上滤
        }

        // 向下调整(堆化)操作,维护堆结构
        void AdjustDown(int parent) {
            Compare com;        // 实例化比较器
            size_t child = parent * 2 + 1;  // 初始左子节点位置

            while (child < _con.size()) {
                // 选择优先级更高的子节点(大顶堆选更大者,小顶堆选更小者)
                if (child + 1 < _con.size() && com(_con[child], _con[child + 1])) {
                    ++child;  // 右子节点优先级更高
                }

                // 关键比较:判断父节点是否需要下沉
                if (com(_con[parent], _con[child])) {
                    swap(_con[child], _con[parent]);  // 交换父子节点
                    // 更新节点位置
                    parent = child;
                    child = parent * 2 + 1;
                }
                else {
                    break;  // 堆条件已满足,退出循环
                }
            }
        }

        // 删除堆顶元素操作
        void pop() {
            // 将堆顶元素与末尾元素交换
            swap(_con[0], _con[_con.size() - 1]);
            _con.pop_back();  // 删除原堆顶元素

            if (!empty()) {
                AdjustDown(0);  // 从根节点开始下滤
            }
        }

        // 访问堆顶元素(常量引用返回)
        const T& top() {
            return _con[0];  // 返回容器首元素(堆顶)
        }

        // 获取队列元素数量
        size_t size() const {
            return _con.size();
        }

        // 判断队列是否为空
        bool empty() const {
            return _con.empty();
        }

    private:
        Container _con;  // 底层存储容器(默认vector)
    };
}
#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<algorithm>

using namespace std;

#include"priority_queue.h"

int main()
{
	priority_queue<int> pq;
	//bit::priority_queue<int, vector<int>, Greater<int>> pq;
	pq.push(4);
	pq.push(1);
	pq.push(5);
	pq.push(7);
	pq.push(9);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

	return 0;
}

这里我们使用到了仿函数,来了解一下:

四).仿函数 

仿函数(Function Object)

  • 一种行为类似函数的对象

  • 通过重载operator()实现函数调用语义

  • 可携带状态(成员变量)的智能函数

// 小于比较仿函数
struct Less {
    template <typename T>
    bool operator()(const T& a, const T& b) const {
        return a < b;
    }
};

// 大于比较仿函数
struct Greater {
    template <typename T>
    bool operator()(const T& a, const T& b) const {
        return a > b;
    }
};

// 使用示例
int main() {
    Less less;
    Greater greater;
    
    std::cout << less(3, 5) << std::endl;    // 输出1 (true)
    std::cout << greater(3, 5) << std::endl;  // 输出0 (false)
    
    return 0;
}

四.deque(双端队列)(了解)


一).核心概念

1. 容器特性

  • 双端队列(Double-ended queue):支持两端高效插入/删除

  • 头文件#include <deque>

  • 核心能力

    • 随机访问(O(1)时间复杂度)

    • 头尾插入/删除(O(1)时间复杂度)

    • 自动扩容(分块连续内存)

  • 内存结构:由多个固定大小的块(buffer)组成的分段连续空间

2. 时间复杂度对比

操作dequevectorlist
头部插入O(1)O(n)O(1)
尾部插入O(1)O(1)*O(1)
中间插入O(n)O(n)O(1)
随机访问O(1)O(1)O(n)
内存连续性分段连续完全连续完全不连续

vector的尾部插入为平摊O(1) 

 二).deque的函数接口

方法说明示例
push_front(value)头部插入d.push_front(10);
push_back(value)尾部插入d.push_back(20);
pop_front()删除头部元素d.pop_front();
pop_back()删除尾部元素d.pop_back();
operator[]随机访问(不检查边界)int x = d[5];
front()访问首元素d.front() = 100;
back()访问末元素d.back() += 50;
size()返回元素数量if(d.size() > 100)
empty()判断是否为空while(!d.empty())

三).实现原理 

  • 中控器(map):存储指向各内存块的指针

  • 数据块(buffer):固定大小的连续内存块(典型大小512字节)

  • 迭代器结构:包含4个指针

    • current:当前元素位置

    • first:当前块起始位置

    • last:当前块结束位置

    • node:中控器中的位置

四).为什么选择deque作为stack和queue的底层默认容器

stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进行操作。

2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的元素增长时,deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

球求了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值