> 作者简介:დ旧言~,目前大二,现在学习Java,c,c++,Python等
> 座右铭:松树千年终是朽,槿花一日自为荣。> 目标:能手撕仿函数模拟
> 毒鸡汤:你活得不快乐的原因是:既无法忍受目前的状态,又没能力改变这一切。
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
我们在vector讲解中已经了解到了priority_queue,只能说是浅谈,priority_queue底层到底是个啥勒?今天带大家揭晓它的面纱。
这里就创建两个文件priority_queue.h(头文件),test.cpp(测试代码文件)
咱们按照下面图解来学习今天的内容:
优先级队列priority_queue,即数据结构中的堆,堆是一种通过使用数组来模拟实现特定结构二叉树的二叉树的数据结构,根据父亲节点与孩子节点的大小关系,可以将堆分为大堆和小堆:
大堆:所有父亲节点的值都大于或等于它的孩子节点的值。
小堆:所有父亲节点的值都小于或等于它的孩子节点的值。
在C++ STL中,priority_queue的声明为:template
其中,每个模板参数的含义为:
使用priority_queue:
- #include
- #include
- #include
-
- int main()
- {
- std::priority_queue<int> maxHeap; //建大堆
- int data[10] = { 56,12,78,23,14,34,13,78,23,97 };
- //让arr中的数据依次入大堆
- for (int i = 0; i < 10; ++i)
- {
- maxHeap.push(data[i]);
- }
-
- std::cout << maxHeap.empty() << std::endl; //判空 -- 0
- std::cout << maxHeap.size() << std::endl; //获取堆中数据个数 -- 10
-
- //依次提取大堆堆顶数据并打输出
- while (!maxHeap.empty())
- {
- //97 78 78 56 34 23 23 14 13 12
- std::cout << maxHeap.top() << " ";
- maxHeap.pop();
- }
- std::cout << std::endl;
-
- std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; //建小堆
- //让arr中的数据依次入小堆
- for (int i = 0; i < 10; ++i)
- {
- minHeap.push(data[i]);
- }
-
- //依次提取堆顶数据并打输出
- while (!minHeap.empty())
- {
- //12 13 14 23 23 34 56 78 78 97
- std::cout << minHeap.top() << " ";
- minHeap.pop();
- }
- std::cout << std::endl;
-
- return 0;
- }
运行结果:
仿函数是使用struct定义的类对象,通过重载操作符(),即operator()(参数列表)实现类似函数的功能。在类中定义运算符()的重载函数,通过类对象,来使调用运算符重载函数的语法,仿函数的调用语法为:类对象名称(参数列表)
在priority_queue的构造函数中,就经常使用less和greater两个仿函数,less和greater都是C++标准库中给出的判断两数之间大小关系的仿函数,他们被包含在头文件
代码实现:
- // 仿函数
- template<class T>
- struct less
- {
- bool operator()(const T& a, const T& b)
- {
- return a < b;
- }
- };
- template<class T>
- struct greater
- {
- bool operator()(const T& a, const T& b)
- {
- return a > b;
- }
- };
构造函数有两种重载形式:
向下调整函数AdjustDown需要有2个参数,分别为:堆中数据个数n和开始向下调整的父亲节点下标parent,其中还会用到类的成员(容器和用于比较的对象),AdjustDown执行的操作依次为(以建大堆为例):
注意:对于开始执行向下操作的父节点parent,一定要保证它的左子树和右子树都为大或小堆。
- // 默认构造函数
- priority_queue()
- {}
- // 通过迭代器区间的构造函数
- template<class InputIterator>
- priority_queue(InputIterator first, InputIterator last)
- :_con(first, last)
- {
- size_t lastchild = size() - 1;
- size_t parent = (lastchild - 1) / 2;
- for (size_t i = parent; i >= 0; i--)
- {
- AdjustDown(i);
- }
- }
- void AdjustDown(size_t parent)
- {
- size_t child = parent * 2 + 1;
- while (child < size())
- {
- //if (child + 1 < size() && _con[child + 1] > _con[child])
- if (child + 1 < size() && com(_con[child], _con[child + 1]))
- ++child;
- //if (_con[child] > _con[parent])
- if (com(_con[parent], _con[child]))
- swap(_con[parent], _con[child]);
- else
- break;
- parent = child;
- child = parent * 2 + 1;
- }
- }
向堆中插入数据需要两步操作:先向容器中尾插数据,然后调用AdjustUp函数上调整数据。
向上调整函数AdjustUp执行的操作流程为:(建大堆为例)
- void push(const T& x)
- {
- _con.push_back(x);
- AdjustUp(size() - 1);
- }
- void AdjustUp(size_t child)
- {
- size_t parent = (child - 1) / 2;
- while (child > 0)
- {
- //if (_con[child] > _con[parent])->父亲小于孩子就调整
- if (com(_con[parent], _con[child]))//
- {
- swap(_con[child], _con[parent]);
- child = parent;
- parent = (child - 1) / 2;
- }
- else
- break;
- }
- }
如果直接将除堆顶之外的全部数据向前移动一个单位,那么数组中剩余的数据大概率无法满足堆的结构要求,重新再建堆效率过低。那么,就需要一些额外的技巧来解决问题:
- void pop()
- {
- swap(_con[size() - 1], _con[0]);
- _con.pop_back();
- AdjustDown(0);
- }
- size_t size() const
- {
- return _con.size();
- }
- T& top()
- {
- return _con[0];
- }
- bool empty()
- {
- return _con.empty();
- }
- #include
- #include
- using namespace std;
-
-
- namespace lyk
- {
- // 仿函数
- template<class T>
- struct less
- {
- bool operator()(const T& a, const T& b)
- {
- return a < b;
- }
- };
- template<class T>
- struct greater
- {
- bool operator()(const T& a, const T& b)
- {
- return a > b;
- }
- };
-
- // container 容器 ,compare 用于调用比较函数的类对象
- template<class T, class Container = vector
, class Compare = less> - class priority_queue
- {
- private:
- Compare com;
- void AdjustUp(size_t child)
- {
- size_t parent = (child - 1) / 2;
- while (child > 0)
- {
- //if (_con[child] > _con[parent])->父亲小于孩子就调整
- if (com(_con[parent], _con[child]))//
- {
- swap(_con[child], _con[parent]);
- child = parent;
- parent = (child - 1) / 2;
- }
- else
- break;
- }
- }
- void AdjustDown(size_t parent)
- {
- size_t child = parent * 2 + 1;
- while (child < size())
- {
- //if (child + 1 < size() && _con[child + 1] > _con[child])
- if (child + 1 < size() && com(_con[child], _con[child + 1]))
- ++child;
- //if (_con[child] > _con[parent])
- if (com(_con[parent], _con[child]))
- swap(_con[parent], _con[child]);
- else
- break;
- parent = child;
- child = parent * 2 + 1;
- }
- }
- public:
- // 默认构造函数
- priority_queue()
- {}
- // 通过迭代器区间的构造函数
- template<class InputIterator>
- priority_queue(InputIterator first, InputIterator last)
- :_con(first, last)
- {
- size_t lastchild = size() - 1;
- size_t parent = (lastchild - 1) / 2;
- for (size_t i = parent; i >= 0; i--)
- {
- AdjustDown(i);
- }
- }
- void push(const T& x)
- {
- _con.push_back(x);
- AdjustUp(size() - 1);
- }
- void pop()
- {
- swap(_con[size() - 1], _con[0]);
- _con.pop_back();
- AdjustDown(0);
- }
- size_t size() const
- {
- return _con.size();
- }
- T& top()
- {
- return _con[0];
- }
- bool empty()
- {
- return _con.empty();
- }
- private:
- Container _con;
- };
- void priority_test1()
- {
- priority_queue<int> pq;
- pq.push(40);
- pq.push(30);
- pq.push(56);
- pq.push(26);
- pq.push(45);
- while (!pq.empty())
- {
- cout << pq.top() << " ";
- pq.pop();
- }
- cout << endl;
- }
- void priority_test2()
- {
- priority_queue<int, vector<int>, greater<int>> pq;
- pq.push(40);
- pq.push(30);
- pq.push(56);
- pq.push(26);
- pq.push(45);
- cout << "greater
建小堆--> " ; - while (!pq.empty())
- {
- cout << pq.top() << " ";
- pq.pop();
- }
- cout << endl;
- priority_queue<int> pq2;
- pq2.push(40);
- pq2.push(30);
- pq2.push(56);
- pq2.push(26);
- pq2.push(45);
- cout << "默认less
建大堆--> " ; - while (!pq2.empty())
- {
- cout << pq2.top() << " ";
- pq2.pop();
- }
- cout << endl;
- }
- }
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。