当前位置: 首页 > news >正文

【C++】栈、队列、双端队列、优先级队列、仿函数

目录

stack

模拟实现

queue

模拟实现

deque 双端队列

priority_queue 优先级队列

模拟实现

优化

仿函数


stack、queue是容器适配器,库里给的默认适配容器是deque
没有迭代器,不支持随便遍历
广度优先遍历要用queue

stack

模板不一定是普通类型,也可能是容器

#include <iostream>
#include <stack>
#include <queue>
using namespace std;void test_stack_queue()
{stack<int> st;st.push(1);st.push(2);st.push(3);st.push(4);while (!st.empty()){cout << st.top() << " ";st.pop();}cout << endl; // 4 3 2 1queue<int> q;q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl; // 1 2 3 4deque<int> dq;dq.push_back(1);dq.push_back(2);dq.push_back(3);dq.push_back(4);dq.push_back(5);dq.push_back(6);for (size_t i = 0; i < dq.size(); i++){cout << dq[i] << " ";}cout << endl; // 1 2 3 4 5 6
}

模拟实现

不用写默认成员函数,因为_con是自定义类型的容器,已经实现好了
我们不写,编译器会自动调用 这个容器的构造、析构、拷贝构造、赋值

stack.h

#pragma once
#include<vector>
#include<list>namespace qtw
{// 容器适配器//template<class T, class Container = vector<T>>template<class T, class Container = deque<T>>class stack{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_back();}T& top(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};void test_stack(){//stack<int, vector<int>> st1;stack<int> st1;st1.push(1);st1.push(2);st1.push(3);st1.push(4);while (!st1.empty()){cout << st1.top() << " ";st1.pop();}cout << endl;stack<int, list<int>> st2;st2.push(1);st2.push(2);st2.push(3);st2.push(4);while (!st2.empty()){cout << st2.top() << " ";st2.pop();}cout << endl;}
}

queue

模拟实现

queue.h

#pragma once
#include<vector>
#include<list>namespace qtw
{// 容器适配器//template<class T, class Container = list<T>>template<class T, class Container = deque<T>>class queue{public:void push(const T& x){_con.push_back(x);}void pop(){_con.pop_front();//_con.erase(_con.begin());}T& front(){return _con.front();}T& back(){return _con.back();}size_t size(){return _con.size();}bool empty(){return _con.empty();}private:Container _con;};void test_queue(){queue<int, list<int>> q;//queue<int, vector<int>> q;//这样写报错,vector没有提供pop_front//所以上面有_con.erase(_con.begin());q.push(1);q.push(2);q.push(3);q.push(4);while (!q.empty()){cout << q.front() << " ";q.pop();}cout << endl;}
}

deque 双端队列

deque 双端队列(严格来说不是队列)。双向开口,两边都可以插入、删除数据的容器。随机迭代器

vector :
        优点:下标随机访问、排序
        缺点:扩容、头部、中间插入删除

list:
        优点:按需申请、任意位置插入删除
        缺点:不支持下标随机访问

deque:库里支持了头尾插删、下标随机访问,是不是完美了呢?不是!

排序消耗的时间,deque拷贝到vector排序再拷贝到deque(拷贝这一下消耗很小) deque对象调用算法sort
算法sort要大量下标访问数据,肯定是 deque 的下标访问没那么快。


看看deque的底层:

中控指针数组满了,像 vector 一样扩容+拷贝数据即可

相比 vector:deque 极大缓解了扩容、头删插问题。但[ ]不够极致,要计算在哪个 buff,在这个 buff 的第几个

operator[](size_t i) // 假设是有效地址
{1.先看在不在第一个buff数组,在就找位置访问2.不在第一个buff,i -= 第一个buff数组的size第几个buff = i/buffsize(每个buff数组的size是固定的)在这个buff的第几个 = i%=buffersize
}

相比 list :
        deque支持下标随机访问
        CPU高速缓存效率不错,不用频繁申请小量内存
        deque头尾删插不错,但中间插入删除很拉胯


总结:deque不适合高频下标随机访问,所以用的不多。但高频的头尾删插 deque 很合适
所以 deque 适配 stack 和 queue 很合适

priority_queue 优先级队列

要给头文件 #include <queue>

也是容器适配器,默认适配容器是 vector。没有提供迭代器,不支持遍历

不是先进先出的队列,是按优先级出的。默认是优先级高
给仿函数 Compare 可以自己控制 大/小 谁的优先级高

底层:堆。做 Top-k 不用写堆,直接用 priority_queue

数组中最大的第K个元素:https://leetcode.cn/problems/kth-largest-element-in-an-array/

void test_priority_queue()
{// 默认是大堆 -- lesspriority_queue<int> pq;// 5 4 3 1// 仿函数控制实现小堆//priority_queue<int, vector<int>, greater<int>> pq;// 1 3 4 5pq.push(3);pq.push(5);pq.push(1);pq.push(4);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;
}

less 是小于的比较,实现的大堆
greater 是小堆

模拟实现

这样就写死了。只能是大堆

priority_queue.h

namespace qtw
{template <class T, class Container = vector<T>>class priority_queue{private:void AdjustDown(int parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child + 1 < _con.size() && _con[child + 1] > _con[child]){child++;}if (_con[child] > _con[parent]){swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}else{break;}}}void AdjustUp(int child){int parent = (child - 1) / 2;while (child > 0){if (_con[parent] < _con[child]){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else{break;}}}public:priority_queue(){ }template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){_con.push_back(*first);first++;}for (int i = (_con.size() - 2) / 2; i >= 0; i--){AdjustDown(i);}}bool empty(){return _con.empty();}size_t size(){return _con.size();}const T& top(){return _con[0];}void push(const T& x){_con.push_back(x);AdjustUp(_con.size() - 1);}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();AdjustDown(0);}private:Container _con;};void test_priority_queue1(){priority_queue<int> pq;pq.push(3);pq.push(5);pq.push(1);pq.push(4);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl;}
}

优化

(先看仿函数第一个分割线以上的部分)

只是向上、向下调整变了

priority_queue.h

namespace qtw
{template <class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{private:void AdjustDown(int parent){Compare com;size_t child = parent * 2 + 1;while (child < _con.size()){//if (child + 1 < _con.size() && _con[child] < _con[child + 1])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 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 test_priority_queue1(){priority_queue<int> pq;// 大堆pq.push(3);pq.push(5);pq.push(1);pq.push(4);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl; // 5 4 3 1}void test_priority_queue2(){priority_queue<int, vector<int>, greater<int>> pq;// 小堆pq.push(3);pq.push(5);pq.push(1);pq.push(4);while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl; // 1 3 4 5}
}

仿函数

就是重载了 operator( ) 的普通类

意义:1. 用库里的控制大堆小堆         2. 如果类型不符合我们的比较意愿,可以控制它

// 仿函数/函数对象
class Less // 防止和库里的less冲突
{
public:bool operator()(int x, int y){return x < y;}
};int main()
{Less lessfunc;cout << lessfunc(1, 2) << endl; // 1cout << lessfunc.operator()(1, 2) << endl; // 1return 0;
}

只看第14行,会觉得lessfunc是函数名

仿函数的真正意义:类对象可以像函数一样使用

库里面把它写成了类模板,可以支持更多类型。前提:这个类型支持了 < 的比较

// 仿函数/函数对象
template <class T>
class Less // 防止和库里的less冲突
{
public:bool operator()(const T& x, const T& y){return x < y;}
};int main()
{Less<int> lessfunc;cout << lessfunc(1, 2) << endl;cout << lessfunc.operator()(1, 2) << endl;return 0;
}

如果是日期类呢?我们重载了日期类的 < >,不会报错

namespace qtw
{template <class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{ };class Date{public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){ }bool operator<(const Date& d)const{return (_year < d._year) ||(_year == d._year && _month < d._month) ||(_year == d._year && _month == d._month && _day < d._day);}bool operator>(const Date& d)const{return (_year > d._year) ||(_year == d._year && _month > d._month) ||(_year == d._year && _month == d._month && _day > d._day);}friend ostream& operator<<(ostream& _cout, const Date& d);private:int _year;int _month;int _day;};ostream& operator<<(ostream& _cout, const Date& d){_cout << d._year << "-" << d._month << "-" << d._day;return _cout;}void test_priority_queue3(){priority_queue<Date> pq;pq.push(Date(2015, 9, 3));pq.push(Date(2019, 10, 1));pq.push(Date(1949, 10, 1));while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl; // 2019-10-1 2015-9-3 1949-10-1}void test_priority_queue4(){priority_queue<Date, vector<Date>, greater<Date>> pq;pq.push(Date(2015, 9, 3));pq.push(Date(2019, 10, 1));pq.push(Date(1949, 10, 1));while (!pq.empty()){cout << pq.top() << " ";pq.pop();}cout << endl; // 1949-10-1 2015-9-3 2019-10-1}
}

有些情况要自己写仿函数

eg:优先级队列里存节点的指针

void test_priority_queue5()
{priority_queue<Date*> pq;pq.push(new Date(2015, 9, 3));pq.push(new Date(2019, 10, 1));pq.push(new Date(1949, 10, 1));while (!pq.empty()){cout << *pq.top() << " ";pq.pop();}cout << endl;
}

每次结果都不一样,为什么?
默认按类型比较排序,类型是指针,就按指针比,且 new 出的地址大小不定

Date* 是内置类型,不能重载运算符

	struct LessPDate // 仿函数控制比较规则{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}};void test_priority_queue6(){priority_queue<Date*, vector<Date*>, LessPDate> pq;pq.push(new Date(2015, 9, 3));pq.push(new Date(2019, 10, 1));pq.push(new Date(1949, 10, 1));while (!pq.empty()){cout << *pq.top() << " ";pq.pop();}cout << endl;}

本篇的分享就到这里了,感谢观看,如果对你有帮助,别忘了点赞+收藏+关注
小编会以自己学习过程中遇到的问题为素材,持续为您推送文章

http://www.dtcms.com/a/430563.html

相关文章:

  • 潢川手机网站建设做网站的图片=gif
  • Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频语义理解与智能检索进阶
  • 图片转视频
  • AI 智能体在 2025 年面临的挑战
  • 做一元夺宝网站需要什么条件网页网站建设软件
  • 网站建设与维护的实训总结wordpress 自定义注册
  • 什么是RDMA?—— 一场网络通信的范式革命
  • 一篇文章入门RabbitMQ:基本概念与Java使用
  • @ResponseStatus 注解详解
  • Linux--权限
  • 【连载3】MySQL 的 MVCC 机制剖析
  • C++封装和继承特性
  • Linux(操作系统)文件系统--对打开文件的管理
  • 【Unity笔记】Unity XR 模式下 Point Light 不生效的原因与解决方法
  • 图片设计网站推荐wordpress下载的主题怎么安装
  • 分布式存储分片核心:从哈希取模到Redis哈希槽,从哈希类到非哈希类
  • C++ 操作 Redis
  • 旅游网站开发文献综述沈阳做网站大约要多少钱
  • 精美个人网站wordpress设置网站主题
  • PyCharm保姆级详细使用手册(Python新手快速上手篇)
  • 3.springboot-容器功能-@注解
  • python开发手机网站开发今天时政新闻热点是什么
  • 【网络编程】深入 HTTP:从报文交互到服务构建,洞悉核心机制
  • java面试0119-java中创建对象的方式?
  • 线程中互斥锁和读写锁相关区别应用示例
  • 网站开发logo绍兴网页设计
  • 2017主流网站风格win7 iis配置网站 视频教程
  • wordpress同步微信公众号seo外包是什么
  • 如何评价一个网站做的好不好展厅网站
  • wordpress站点克隆vip影视建设网站官网