通过栈/队列/优先级队列/了解容器适配器,仿函数和反向迭代器

news/2024/12/5 11:48:18/

文章目录

    • 一.stack
    • 二.queue
    • 三.deque(双端队列)
    • 四.优先级队列
      • 优先级队列中的仿函数
      • 手搓优先级队列
    • 五.反向迭代器
      • 手搓反向迭代器

vector和list我们称为容器,而stack和queue却被称为容器适配器。

在这里插入图片描述

这和它们第二个模板参数有关系,可以看到stack和queue的第二个模板参数的缺省值都是deque,即双端队列容器。也就是说栈和队列都是以容器为主材料构建的,而且因为第二个参数是一个缺省值,我们不但可以使用deque构建,同样可以使用vectorlist来构建。

用已经存在的容器来封装转换成不存在的容器,这种方式就被称之为适配器模式。

有了deque提供的接口,再要实现栈和队列就会变得很简单。

一.stack

栈的特点就是后进先出,,插入和删除都是在尾部进行,栈不提供迭代器(因为栈只能访问栈顶的元素)。

#include<deque>
namespace wbm
{template <class T, class Container = deque<T>>class stack{public:bool empty()const{return _con.empty();}void push(const T&x){_con.push_back(x);}void pop(){_con.pop_back();}size_t size()const{return _con.size();}const T& top()const{return _con.back();}private:Container _con;};}

栈不但可以通过deque来封装,还可以通过vectorlist来封装,只要支持尾插尾删即可

二.queue

队列的特点是先进先出,队尾入,队头出,可以访问队头和队尾的数据,也不提供迭代器

#include<deque>namespace wbm
{template<class T,class Container=deque<T>>class queue{public:bool empty()const{return _con.empty();}void push(const T& x){_con.push_back();}void pop(){_con.pop_front();}size_t size()const{return _con.size();}const T& fornt()const{return _con.front();}const T& back()const{return _con.back();}private:Container _con;};
}

只要是支持尾插头删的容器都可以做queue的适配器,但是不建议使用数组,因为数组的头删效率太低

三.deque(双端队列)

因为vector和list都各有优缺点,因此大佬们就想是否可以创造一个容器兼具vector和list的优点又避免缺点,于是便有了双端队列deque,虽然deque的名字带着队列,但是它和队列毫无关系。

在这里插入图片描述

观察它提供的接口发现:它既支持随机访问,又支持头插头删和尾插尾删,看起来似乎确实是一个完美的容器。但如果真的那么完美,vector和list岂不是早就被淘汰了。

其实deque的底层是一个指针数组+多个buffer数组(buffer数组的大小是固定的)的组成形式;这个指针数组是一个中控数组,其中存放的元素是属于这个deque的buffer数组的地址。

第一次插入数据开辟的buffer数组的地址存放在中控数组的中间元素,如果buffer数组满了要继续尾插,则继续开辟新的buffer数组,此时第二个buffer数组的地址,存放在中控数组中间元素的下一个。如果你要头插,则开辟一个新的buffer数组,并将这个buffer数组的地址存放在中间元素的前一个。

在这里插入图片描述

deque的优点:

1.扩容代价小于vector:它只有在中控满了以后才需要扩容,并且不需要拷贝原生数据,只要将中控数组与buffer之间的映射关系拷贝下来即可。

2.因为是一段一段的空间,所以CPU高速缓存命中率高于list。

3.头尾插入删除效率都较高,并且支持随机访问

但是deque也有自己的缺点:

1.随机访问效率不如vector,它的随机访问要通过计算得到,假设每个buffer数组的大小为size,你要访问第10个元素,首先要10/size来确定这个元素在哪一个buffer数组中,再用10%size来确定这个元素在这个buffer数组中的哪个位置,所以deque也不适合排序,因为排序需要大量的随机访问。

2.中间插入删除不如list,它在中间插入删删同样需要挪动一定量的数据

3.迭代器实现复杂:它的迭代器中有四个指针,当cur==last时,说明本段空间被使用完毕++node继续去访问下一段空间,并更新firstlast

在这里插入图片描述


四.优先级队列

在这里插入图片描述

优先级队列的特点就是优先级高的先出,它也是一个容器适配器,不提供迭代器,底层是一个堆并且默认是大堆。

priority_queue<int> //小堆
priority_queue<int,vector<int>,greater<int>> //大堆

优先级队列中的仿函数

仿函数是一个函数对象,它是一个类函数的对象,要达到类函数,所以这个类中必须要重载()。优先级队列中默认是大堆,如果我们要改成小堆,除了要显示传递第三个参数以外还要更改比较大小的算法。在C语言中,为了能让qsort排序任意类型,库中使用了函数指针的办法,让使用者显示的去写一个比较函数,并将该函数的地址作为参数传递过去。仿函数的一个应用场景就类似于函数指针。

namespace wbm
{template<class T>struct less //可以使用struct,也可以使用class,它们都是类,只是默认的访问限定不同{bool operator()(const T& x, const T& y){return x < y;}};template <class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};
}
int main()
{wbm::less<int> func;//两者等价:func(1, 2)==func.operator()(1, 2);return 0;
}

手搓优先级队列

#include<vector>
#include<iostream>
namespace wbm
{template<class T>struct less //可以使用struct,也可以使用class,它们都是类,只是默认的访问限定不同{bool operator()(const T& x, const T& y){return x < y;}};template <class T>struct greater{bool operator()(const T& x, const T& y){return x > y;}};template<class T, class Container =std:: vector<T>,class Compare = less<T>>class priority_queue{private:void Adjustdown(size_t 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;//默认是less,也就是左孩子比右孩子小}//将孩子节点和父节点做比较if (com(_con[parent], _con[child])) {std::swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else{break;}}}void Adjustup(size_t child){Compare com;size_t parent = (child - 1) / 2;while (child > 0){if (com(_con[parent], _con[child])){std::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):_con(first, last)  //初始化列表,vector支持迭代器区间构造{//初始化建堆,使用向下调整,从第一个非叶子节点开始for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--){Adjustdown(i);}}void push(const T& x){_con.push_back(x);Adjustup(_con.size() - 1);}void pop(){//将首元素和末尾元素换位置删除后调整std::swap(_con.front(), _con.back());_con.pop_back();Adjustdown(0);}bool empty()const{return _con.empty();}size_t size()const{return _con.size();}const T& top()const{return _con.front();}private:Container _con;};
}

如果优先级队列中存放的是某个类的地址,又需要我们比较地址中值的优先级,那就需要使用仿函数来进行特殊处理。否则只会按照地址来比较。

五.反向迭代器

反向迭代器采用的是适配器模式,是通过正向迭代器的再封装实现的,你给它某个容器的正向迭代器,它就产生这个容器的反向迭代器,它与正向迭代器的位置是对称的并且正好相反。所以要控制反向迭代器,只需要使用运算符重载,篡改方向迭代器中++--的规则就可以。

reverse_iterator rbegin(){return reverse_iterator(end());}
reverse_iterator rend(){return reverse_iterator(begin());}

在这里插入图片描述

rbegin和end一样,指向的是最后一个元素的下一个位置,要想访问到3,要先--再访问,这个问题可以通过重载*来解决

template<class Iterator,class Ref,class Ptr>
Ref operator*()
{Iterator tmp=it;return *(--tmp);
}

手搓反向迭代器

namespace wbm
{//反向迭代器,使用正向迭代器封装template<class Iterator,class Ref,class Ptr>  //迭代器,T&/const T&,T*/const T*class ReverseIterator{typedef ReverseIterator<Iterator, Ref, Ptr> Self;public:ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp;return *(--tmp);}Ptr operator->(){return &(operator*())}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator!=(const Ref& rit)const//两个迭代器之间的比较{return _it != rit;}private:Iterator _it;//这里给的参数是一个正向迭代器};
}

http://www.ppmy.cn/news/64412.html

相关文章

csgo搬砖项目,时间自由,项目包下车,包落地

Steam是一款全球较大的综合性数字游戏软件发行平台。steam同时在线飙到3300万&#xff01;超越你说熟悉的王者&#xff0c;吃鸡&#xff01;用户多&#xff0c;竞争者少&#xff0c;连我自己都没想到&#xff0c;有一天我居然可以靠着steam游戏搬砖来赚钱养活自己。 实话实说&a…

【分布族谱】Zipf分布及其Python可视化

文章目录 zipf分布简介zipfian和zipf对象zipf分布到zeta分布的变化情况分布族谱图 zipf分布简介 #mermaid-svg-mG901pJXpTYFT7Bk {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-mG901pJXpTYFT7Bk .error-icon{fill:…

一文带你搞清 ChatGPT 与 Azure OpenAI 的区别

这两周是我从2017年开始全职涉入 NLP 领域后最忙的两周&#xff0c;无数的同事和客户都在向我提出一个询问&#xff1a;ChatGPT 可以帮到我们什么&#xff1f; 特别是在2023年3月31日我做了一场微软 Azure OpenAI [布局助力企业]拥抱新智能时代的演讲之后&#xff0c;这几天我…

【大数据之Hadoop】二十七、生产调优-HDFS多目录

1 NameNode多目录配置 NameNode本地目录可以配置多个&#xff0c;每个目录存放内容相同&#xff0c;增加可靠性。 在hdfs-site.xml中添加&#xff0c;每台服务器节点的磁盘不同&#xff0c;可以选择不分发。 <property><name>dfs.namenode.name.dir</name>…

AssetBundle加载与卸载时的内存变化

AssetBundle.LoadFromFile加载一个80MB的assetbundle会分配1MB左右的pss内存 adb分析&#xff1a;private-otherUnityProfiler分析&#xff1a;有3块 1.Other/AssetBundle/LoadingCache 2.Other/SerializedFile/archive:/CAB-e42axxxxxxx 3.NotSaved/AssetBundle/xxxxxx.ab …

QT设置widget属性为FramelessWindowHint导致界面刷新的问题

一.问题描述 当使用继承自QWidget的QT对象时&#xff0c;如果设置了窗口风格&#xff08;FramelessWindowHint&#xff09;为无边框&#xff0c;则在使用 包括 窗口最大化、windows系统&#xff08;winD&#xff09;&#xff0c;图标来回点击显示等操作时&#xff0c;导致界面…

2023年,网络安全方面 5 大值得学习的编程语言

Python 到目前为止&#xff0c;Python 在网络安全领域一直处于领先地位。这是一种通用的服务器端脚本语言&#xff08;无需编译&#xff09;&#xff0c;已经被应用到成千上万的安全项目中。你会发现绝大多数安全工具和 PoCs 都是用 Python 编写的&#xff0c;这样做是有充分理…

【Hackthebox Stocker】打靶记录

Hackthebox Stocker nmap 扫描一把 得到tcp端口22 80 nmap -sC -sV 10.10.11.196 Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-10 05:51 EDT Nmap scan report for 10.10.11.196 Host is up (0.25s latency). Not shown: 998 closed tcp ports (reset) PORT STAT…