实现优先队列——C++

ops/2024/9/23 1:36:09/

目录

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/ops/30096.html

相关文章

React Context

Context https://juejin.cn/post/7244838033454727227?searchId202404012120436CD549D66BBD6C542177 context 提供了一个无需为每层组件手动添加 props, 就能在组件树间进行数据传递的方法 React 中数据通过 props 属性自上而下(由父及子)进行传递&#xff0c;但此种用法对…

区块链 | IPFS:Merkle DAG(进阶版)

&#x1f98a;原文&#xff1a;Merkle DAGs: Structuring Data for the Distributed Web &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。 1 Merkle DAG 当我们在计算机上表示图时&#xff0c;必须通过提供节点和边的具体表示来编码我们的数据…

【通信中间件】Fdbus HelloWorld实例

Fdbus实例教程 Fdbus简介 Fdbus 全称 Fast Distributed Bus&#xff08;高速分布式总线&#xff09;&#xff0c;提供IPCRPC功能。适用于多种OS&#xff1a; LinuxQNXAnroidOSWindow Fdbus本质是Socket&#xff0c;IPC基于Unix domain socket&#xff0c;RPC基于TCP。使用G…

strcpy,strncpy函数详解

strcpy函数 在C语言中&#xff0c;strcpy()函数用于将一个字符串复制到另一个字符串中。 函数原型如下&#xff1a; char *strcpy(char *destination, const char *source);参数解释&#xff1a; destination&#xff1a;目标字符串&#xff0c;将会被复制到。source&#…

数学建模--图论最短路径基础

1.图的定义 学过数据结构或者离散数学的小伙伴们应该知道图的概念&#xff0c;我在这里简单的介绍一下&#xff1a; 图的概念和我们理解的是很不一样的&#xff0c;这里的图并不是我们的生活里面的图片&#xff0c;而是一种表示不同的数据之间的关系&#xff0c;例如这里的5个…

Django框架之视图层

一、三板斧的原理介绍 1、HttpResponse 在Django中&#xff0c;HttpResponse是一个类&#xff0c;用于构建HTTP响应并返回给客户端。当视图函数处理完请求后&#xff0c;需要返回一个响应时&#xff0c;就会使用HttpResponse对象。 &#xff08;1&#xff09;创建HttpRespon…

【EI会议|稳定检索】2024年传感技术与图像处理国际会议(ICSTIP 2024)

2024 International Conference on Sensing Technology and Image Processing 一、大会信息 会议名称&#xff1a;2024年传感技术与图像处理国际会议会议简称&#xff1a;ICSTIP 2024收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等会议官网&#xff1a;htt…

Vue阶段练习:组件拆分

页面开发思路 分析页面&#xff0c;按模块拆分组件&#xff0c;搭架子&#xff08;局部或全局注册&#xff09;根据设计图&#xff0c;编写html结构css样式拆分封装通用小组件&#xff08;局部或全局注册&#xff09;将来通过js动态渲染实现功能 BaseBrandItem.vue <templ…