实现优先队列——C++

news/2024/9/25 16:04:24/

目录

1.优先队列的类模板

2.仿函数的讲解

3.成员变量

4.构造函数

5。判空,返回size,返回队头

6.插入

7.删除


1.优先队列的类模板

我们先通过模板来进行初步了解

由上图可知,我们的模板里有三个参数,第一个参数自然就是你要存储的数据的类型了;第二个参数是我们的适配器,适配器可以手动更改,但我们这里就用它默认的vector,这就意味着我们的优先队列是由我们的顺序表容器来实现的;第三个参数是我们的仿函数,仿函数是我们这个优先队列的重要知识点,等会我会详细说明。

我先把代码全放出来,这样方便不理解的时候可以看到全部代码来思考。

我提前说明一下,优先队列的本质其实就是vector和堆的性质。

template<class T>class less{public:bool operator()(const T& a, const T& b){return a < b;}};template<class T>class greater{public:bool operator()(const T& a, const T& b){return a > b;}};template<class T,class Container=vector<T>,class comper=less<T>>class priority_queue{public://构造priority_queue(){}//拷贝构造//模板函数//迭代器版template <class InputIterator>priority_queue(InputIterator first, InputIterator last){while (first != last){push(*first);first++;}}//判空bool empty() const{return _con.empty();}//返回sizesize_t size() const{return _con.size();}//返回队头const T& top(){return _con[0];}//插入//向上调整void adjustup(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}elsebreak;}}void push(const T& x){_con.push_back(x);adjustup(_con.size()-1);}//删除//向下调整void adjustdown(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size() && cmp(_con[child], _con[child+1])){child++;}if (cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjustdown(0);}private:Container _con;comper cmp;};

2.仿函数的讲解

仿函数是什么意思呢,我们不妨猜测一下,通过它的名字,仿函数似乎是一个模仿函数的存在,这个猜测其实就是对的。仿函数没有什么门道,它的底层就是进行了一个运算符重载,重载的是(),所以调用的时候感觉像是在调用函数一样。

template<class T>class less{public:bool operator()(const T& a, const T& b){return a < b;}};

我们可以通过上面的代码发现我们的类叫less,里面重载了一个()运算符,使其能达到判断较小值的目的;

template<class T>class greater{public:bool operator()(const T& a, const T& b){return a > b;}};

有了判断较小值,自然就有判断较大值,原理也是跟上面一样。

有了仿函数我们就能更加方便且安全的对自定义类型的数据进行有意义的大小比较。

3.成员变量

private:Container _con;comper cmp;

成员变量我们自然还是设置成私有的,这两个成员变量的类型是不是有一点懵逼?其实是我们的类模板声明的。

template<class T,class Container=vector<T>,class comper=less<T>>

所以我们的第一个成员变量的类型是我们的vector,第二个是我们的仿函数类型的数据。

4.构造函数

//构造priority_queue(){}

其实我们仔细想一想就能明白,我们的优先队列的底层既然是vector和堆,而我们的优先队列存在的意义就像是给vector再加工一下,实现堆的性质和功能,所以我们的优先队列甚至不需要自己实现构造函数,直接让它自动调用vector的就行了。析构函数也是一样的道理,我们既然不需要写构造函数,那么析构函数直接不定义了,调用系统默认的就可以了。

5。判空,返回size,返回队头

//判空bool empty() const{return _con.empty();}//返回sizesize_t size() const{return _con.size();}//返回队头const T& top(){return _con[0];}

这三个功能真的没有什么好解释的,大家一看就能理解,这不都是复用了vector的功能嘛。

6.插入

//插入//向上调整void adjustup(size_t child){size_t parent = (child - 1) / 2;while (child > 0){if (cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}elsebreak;}}void push(const T& x){_con.push_back(x);adjustup(_con.size()-1);}

插入功能就是这个优先队列不同于顺序表的地方之一了,因为是堆的性质,所以我们插入的时候肯定不能单纯的插入,我们要进行向上调整,向上调整的目的就是把我们的vector打造成堆的模样。我们就来讲解一下向上调整

我们这次建的是大堆,大堆是什么意思呢?大堆就是我们的父亲节点要大于我们的孩子节点,所以如果我们的父亲节点小于孩子节点我们就交换父亲节点和孩子节点的值。

仿函数的用法就出现了

cmp(_con[parent], _con[child])

不说的话大家是不是就以为cmp是我们的普通函数了?仿函数的妙用就在这里。

然后我再说明一下,孩子节点和父亲节点是如何来确定的,我们知道顺序表的物理结构也是连续的,下标从0开始,我们的父亲节点只需要*2加1就能找到左孩子,再加一就能找到右孩子,而我们的不管是左孩子还是右孩子都只需要-1再除2就能找到父亲了,这其中的数学原理大家下去画画图,很快就能得出这个结论了。

7.删除

//删除//向下调整void adjustdown(size_t parent){size_t child = parent * 2 + 1;while (child < _con.size()){if (child+1<_con.size() && cmp(_con[child], _con[child+1])){child++;}if (cmp(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}elsebreak;}}void pop(){swap(_con[0], _con[_con.size() - 1]);_con.pop_back();adjustdown(0);}

删除用的就是我们的向下调整了。我们的优先队列的数据是连续的,从头部删除性能肯定不好,所以我们的思路是先把要删除的元素跟最后一个交换,然后删除最后一个元素就能达到我们的效果了,这个时候为了保证我们堆的性质要对首元素进行调整,调整到它该去的位置。


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

相关文章

华为OD试题之第k长子串

第k长子串 题目描述 给定一个字符串 只包含大写字母 求在包含同一字母的子串中 长度第K长的子串 相同字母只取最长的子串 输入描述 第一行 一个子串 1 < len < 100 只包含大写字母 第二行为k的值 输出描述 输出连续出现次数第k多的字母的次数 如果子串中只包含同一字母…

ctfshow——SSRF

文章目录 web 351web 352web 353web 354web 355web 356web357web 358web 359web 360 SSRF(Server-Side Request Forgery&#xff1a;服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统…

uniapp 异步加载级联选择器(Cascader,data-picke)

目录 Props 事件方法 inputChange事件回调参数说明&#xff1a; completeChange事件回调参数说明&#xff1a; temList 属性Object参数说明 defaultItemList 属性Object参数说明 在template中使用 由于uniapp uni-ui的data-picke 不支持异步作者自己写了一个 插件市场下…

存在矛盾的题目

{ u t t − a 2 u x x 0 , t > 0 , x > 0 , u ( x , 0 ) sin ⁡ ( x ) 2 x , x ≥ 0 , u t ( x , 0 ) cos ⁡ ( x ) , x ≥ 0 , u x ( 0 , t ) 2 , t ≥ 0. \begin{cases} u_{tt} - a^2 u_{xx} 0, & t > 0, x > 0, \\ u(x, 0) \sin(x) 2x, & x \ge…

ThreeJS:坐标辅助器与轨道控制器

ThreeJS与右手坐标系 使用ThreeJS创建3D场景时&#xff0c;需要使用一个坐标系来定位和控制对象的位置和方向。 ThreeJS使用的坐标系是右手坐标系&#xff0c;即&#xff1a;X轴向右、Y轴向上、Z轴向前&#xff0c;如下图所示&#xff0c; ThreeJS-右手坐标系 Tips&#xff1a;…

【linux运维】vim基础应用

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了学习基本的shell编程和linux命令&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于b站大学——linux运维课程进行的&#xff0c;…

软件杯 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

SQL 基础 | AS 的用法介绍

SQL&#xff08;Structured Query Language&#xff09;是一种用于管理和操作数据库的标准编程语言。 在SQL中&#xff0c;AS关键字有几种不同的用法&#xff0c;主要用于重命名表、列或者查询结果。 以下是AS的一些常见用法&#xff1a; 重命名列&#xff1a;在SELECT语句中&a…