当前位置: 首页 > 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.lryc.cn/news/609024.html

相关文章:

  • Java 大视界 -- Java 大数据机器学习模型在金融市场情绪分析与投资决策辅助中的应用(379)
  • 控制建模matlab练习05:比例积分控制-①系统建模
  • 【游戏比赛demo灵感】Scenario No.9(又名:World Agent)
  • 【Python✨】解决 Conda 安装 MoviePy 报错问题
  • 【Linux系统编程】进程信号
  • Rust 同步方式访问 REST API 的完整指南
  • python学智能算法(三十一)|SVM-Slater条件理解
  • Rust:如何开发Windows 动态链接库 DLL
  • 【AI编程工具IDE/CLI/插件专栏】-国外IDE与Cursor能力对比
  • 08.Redis 持久化
  • Pytorch实现一个简单的贝叶斯卷积神经网络模型
  • (一)全栈(react配置/https支持/useState多组件传递/表单提交/React Query/axois封装/Router)
  • CICD--自动化部署--jinkins
  • TV电视版软件集合分享
  • 动感按钮:如何打造交互感十足的点击动画效果
  • 【前端安全】聊聊 HTML 闭合优先级和浏览器解析顺序
  • 二叉树算法之【前序遍历】
  • 设计原则和设计模式
  • 图像、视频、音频多模态大模型中长上下文token压缩方法综述
  • 【Leetcode】2106. 摘水果
  • 【openlayers框架学习】九:openlayers中的交互类(select和draw)
  • 安卓调javaScript Not find method “forceLogout“ implementatidsignature or namesp
  • 【C语言符号单词搜索首位置及数量】2022-10-4
  • web前端React和Vue框架与库安全实践
  • 数组和指针的关系
  • 【LeetCode刷题指南】--二叉树的后序遍历,二叉树遍历
  • VUE父级路由没有内容的解决方案
  • Python自动化测试框架:Unittest 断言
  • 数据结构中使用到的C语言
  • elk快速部署、集成、调优