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

C++ 之 【模拟实现 优先级队列】

目录

1.前提准备

2.完整模拟实现

2.1构造函数

2.2向下调整法

2.3 push 及 向上调整法

2.4 pop

2.5其他操作

3.自定义仿函数的需求


1.前提准备

(1)

创建一个PriorityQueue.h文件来保存模拟实现的优先级队列

同时,因为C++库里面已经有了优先级队列的实现,所以我们为了避免命名冲突

创建一个新的命名空间来封装模拟实现的优先级队列

(2)

与STL库保持一致,模拟实现时默认使用容器 vector 和比较器 less

同时将 作为优先级队列的底层数据结构

namespace dfq1
{template<class T, class Container = vector<T>, class Compare = less<T>>class priority_queue{//.......
private:Container _con;Compare _com;
};
}

2.完整模拟实现

2.1构造函数

//默认构造:创建空的优先级队列priority_queue(){ }//区间创建优先级队列template<class InputIterator>priority_queue(InputIterator first, InputIterator last){//进数据while (first != last){_con.push_back(*first);++first;}//默认建大堆for (int i = (_con.size() - 1 - 1) / 2; i >= 0; --i){AdjustDown(i);}}

优先级队列拥有两个构造函数,因为我们显示写了区间创建优先级队列的构造函数,所以我们也需要手写无参的构造函数,这样在创建空的优先级队列时才会有默认构造函数被调用

默认构造函数会自动调用其成员的构造函数以完成初始化

区间创建优先级队列时,我们将迭代器作为模板参数,这样就可以做到类型通用

首先进数据,因为是vector作为底层容器,所以直接尾插 push_back

然后建堆,优先选择向下调整法建堆     (不懂概念的朋友可进入我的主页搜索  堆   进行了解)

2.2向下调整法

void AdjustDown(int parent){int child = parent * 2 + 1;//孩子存在就比较while (child < _con.size()){//判断左右孩子,左孩子比右孩子“”,++childif (child + 1 < _con.size() && _com(_con[child], _con[child + 1]))++child;//父亲比孩子“”,就交换.......if (_com(_con[parent], _con[child])){swap(_con[parent], _con[child]);parent = child;child = parent * 2 + 1;}else    break;}
}

向下调整法的思想、代码逻辑这里不再赘诉 ,(不懂概念的朋友可进入我的主页搜索 堆 进行了解)

C语言中,根据建堆的需要,大小堆使用的向下调整法需要各写一份,此时C++传入比较器的野心就显现了,同一份代码,仅仅比较器不同就可以实例化出不同的向下调整法

建大堆时,

父亲节点需要孩子节点中的较大节点进行比较,左孩子小于右孩子,child++

parent 小于 child 此时就需要进行交换等一系列后续操作

所以 建大堆 用 less 

建小堆时,

父亲节点parent需要孩子中的较小节点进行比较,左孩子大于右孩子,child++

parent 大于 child 此时就需要进行交换等一系列后续操作

所以 建小堆 用 greater 

_com是函数对象(仿函数)的实例,其可以像普通函数一样进行使用

_con 默认是 vector<T> 的对象,即可以根据下标进行随机访问操作

 

_com(_con[child], _con[child + 1])

这里就是在比较_con[child] 与_con[child + 1] 的大小并返回bool值

2.3 push 及 向上调整法

void push(const T& val){//进数据_con.push_back(val);//向上调整AdjustUp(_con.size() - 1);
}
void AdjustUp(int child){int parent = (child - 1) / 2;//parent始终大于等于0while (child > 0){//父亲比孩子“”,就交换.......if (_com(_con[parent], _con[child])){swap(_con[parent], _con[child]);child = parent;parent = (child - 1) / 2;}else    break;}
}

向上调整法的思想、代码逻辑这里不再赘诉 ,(不懂概念的朋友可进入我的主页搜索 堆 进行了解)

插入数据的思路就是先插入到尾部,再进行向上调整操作

大堆进行向上调整时

父亲节点小于孩子节点才进行交换等一系列后续操作,所以 大堆 用 less

小堆进行向上调整时

父亲节点大于孩子节点才进行交换等一系列后续操作,所以 小于堆 用 greater

_com是函数对象(仿函数)的实例,其可以像普通函数一样进行使用

_con 默认是 vector<T> 的对象,即可以根据下标进行随机访问操作

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

这里就是在比较_con[parent] 与_con[child] 的大小并返回bool值

2.4 pop

void pop()
{swap(_con[0], _con[_con.size() - 1]);//出数据_con.pop_back();//向下调整AdjustDown(0);
}

删除数据就是删除堆顶数据,思路就是首尾交换->尾删->向下调整

2.5其他操作

bool empty()const{return _con.empty();
}size_t size()const{return _con.size();
}T& top(){return _con[0];
}const T& top()const{return _con[0];
}

这些操作实现的是对适配容器的封装,底层实现时直接调用即可

3.自定义仿函数的需求

less、greater已经满足大多数优先级队列的创建要求,但还是有些例外,如下

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;
}

上述代码是自定义类型Date类

void test_Date(){priority_queue<Date> pq;pq.push(Date(2025, 8, 1));pq.push(Date(2025, 5, 1));pq.push(Date(2025, 9, 1));while (!pq.empty()){cout << pq.top() << ' ';pq.pop();}cout << endl;priority_queue<Date*> pq1;pq1.push(new Date(2025, 8, 1));pq1.push(new Date(2025, 5, 1));pq1.push(new Date(2025, 9, 1));while (!pq1.empty()){cout << *pq1.top() << ' ';pq1.pop();}
}

上述测试用例中,

pq存储的类型是Date,而Date类重载了>、<运算符,使得pq得以根据需要进行创建

pq1存储的类型是Date*,仿函数的对象实例只会根据地址大小创建优先级队列,不符合我们根据年月日大小进行建堆的期望

所以此时需要自定义仿函数

struct LessPDate{bool operator()(const Date* p1, const Date* p2){return *p1 < *p2;}
};priority_queue<Date*, vector<Date*>, LessPDate> pq1;

总结:

当优先级队列存储自定义类型时,该类型需重载 < 或 > 运算符,以便默认的 less 、 greater仿函数能够正确比较元素

若存储的是指针类型,由于指针的比较行为可能与需求不符(如默认按地址比较),此时需自定义仿函数来定义排序规则

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

相关文章:

  • SQL 地理空间原理与实现
  • slice() 和 splice()
  • 信创及一次ORACLE到OB的信创迁移
  • 自由学习记录(76)
  • Python 的标准库 bisect 模块
  • 源码交易平台排行榜
  • 机器学习 决策树基本介绍
  • Mysql的MVCC是什么
  • HCIE-Datacom题库_07_设备【道题】
  • 《深入解析 Python 的 `*args` 和 `**kwargs`:从基础使用到高级应用》
  • 【数据结构】哈希表实现
  • 网关和BFF是如何演化的
  • uniapp 跨端开发
  • 基于Springboot+UniApp+Ai实现模拟面试小工具八:管理端基础功能实现
  • (论文速读)探索多模式大型语言模型的视觉缺陷
  • DeepSeek 论文夺冠,智谱开源 GLM-4.5,OpenAI 学习模式上线!| AI Weekly 7.28-8.3
  • 基于机器学习的Web应用漏洞分析与预测系统,使用django框架,三种机器学习模型
  • 深入探讨AI在测试领域的三大核心应用:自动化测试框架、智能缺陷检测和A/B测试优化,并通过代码示例、流程图和图表详细解析其实现原理和应用场景。
  • 关于Web前端安全防御之内容安全策略(CSP)
  • 知识蒸馏 - 基于KL散度的知识蒸馏 HelloWorld 示例 采用PyTorch 内置函数F.kl_div的实现方式
  • 【Linux系统】进程间通信:匿名管道
  • AI 时代的 IT 从业者:共生而非替代
  • 人声伴奏分离API:音乐智能处理的强大工具
  • Spring AI 项目实战(二十二):Spring Boot + AI +DeepSeek实现智能合同数据问答助手​(附完整源码)
  • 小白学OpenCV系列2-理解图像
  • MySQL--高可用MHA集群详解及演练
  • SelectDB数据库,新一代实时数据仓库的全面解析与应用
  • CICD--自动化部署--jinkins
  • 深度学习中的三种Embedding技术详解
  • OSPF知识点整理