Table of contents
priority queue priority_queue
Priority queue priority_queue is a container adapter that defaults to the first element being the largest among the elements it contains according to strict weak ordering criteria.
The priority queue uses vector as the underlying container for storing data by default, and uses the heap sorting algorithm to sort the data on the vector, so priority_queue
it is a heap. Note: priority_queue is large by default
priority_queue
It is stored <queue>
in the file. To use it, you only need to package queue
the file.
So if we need to use the location of the heap in the future, we don't need to manually implement a heap, we can just use it directly
priority_queue
.
The following is the definition of priority queue:
template <class T, class Container = vector<T>,
class Compare = less<typename Container::value_type> > class priority_queue;
The first template parameter is the type of the element placed in the adapter container.
The second template parameter is the adapter, which defaults to vector.
The third template parameter is a functor, which defaults to less. In order to ensure that the default is a large heap
function | Interface Description |
---|---|
priority_queue() | Construct an empty priority queue |
priority_queue (InputIterator first, InputIterator last) | Construct a priority queue using the [first, last) iterator interval |
empty() | Call it short |
top( ) | Returns the largest (smallest element) in the priority queue, that is, the top element of the heap |
push(x) | Insert element x into the priority queue |
pop() | Delete the largest (smallest) element in the priority queue, that is, the top element of the heap |
Let's try it using a priority queue:
void test1()
{
priority_queue<int> pq;
pq.push(1);
pq.push(6);
pq.push(9);
pq.push(3);
pq.push(0);
pq.push(2);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}//输出 9 6 3 2 1 0
cout << endl;
}
If we implement a small heap first, we only need to change the third template parameter, so that the third template parameter is. greater<T>
Because we specified the third template parameter, we also need to manually pass the second template parameter explicitly.
Next we implement a small heap priority_queue
:
void test1()
{
priority_queue<int,vector<int>,greater<int>> pq;
pq.push(1);
pq.push(6);
pq.push(9);
pq.push(3);
pq.push(0);
pq.push(2);
while (!pq.empty())
{
cout << pq.top() << " ";
pq.pop();
}//输出 0 1 2 3 6 9
cout << endl;
}
Simulation implementation of priority_queue
Because the bottom layer of the priority queue is heap sort, we first complete the downward adjustment and upward adjustment in heap sort.
void AdjustDown(int parent)
{
Compare com;
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child]<_con[child + 1])
{
child++;
}
if (_con[parent]< _con[child])
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
Compare com;
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (_con[parent]< _con[child])
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
The push function first inserts an element into the underlying adapter and then calls the upward adjustment method.
void push(const T& t)
{
_con.push_back(t);
AdjustUp(_con.size() - 1);
}
The pop function first exchanges the first element and the last element in the adapter, then deletes the last element, and then calls the downward adjustment method.
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
There are also several functions that are very simple to implement:
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
Current complete code:
namespace my_priority_queue
{
template<class T, class Container = std::vector<T>>
class priority_queue
{
private:
void AdjustDown(int parent)
{
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && _con[child]<_con[child + 1])
{
child++;
}
if (_con[parent]< _con[child])
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (_con[parent]< _con[child])
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}
public:
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);
}
}
void push(const T& t)
{
_con.push_back(t);
AdjustUp(_con.size() - 1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con[0];
}
size_t size()
{
return _con.size();
}
private:
Container _con;
};
}
At this point, we have implemented a large heap priority queue.
So how to implement a small heap priority queue? Currently we can only modify the smaller parts of the downward adjustment and upward adjustment:
This is actually quite troublesome. Is there any way to switch between the priority queues of the large heap and the small heap in seconds?
The answer is: you can use functors
Functor
Functors are also called function objects. Objects of this class can be used like functions, making the use of a class look like a function.
Its implementation is to implement one in the class operator()
. With the overloading of brackets, objects of this class can be Use like a function
Here is a functor:
class IntLess
{
public:
bool operator()(int x,int y)
{
return x < y;
}
};
How to use functors? After all, a functor is a class, so when we use it, we need to instantiate an object first
and then use this object like a function .
Add it after the object ()
and pass the parameters as needed.
void test2()
{
IntLess LessFunc;
cout<<LessFunc(1,2)<<endl;//仿函数的使用
}
LessFunc(1,2)
will be processed into:LessFunc.operator()(1,2)
Next, use functors to improve the priority queue of the simulated implementation.
template<class T>
struct Less//首字母大写,为了与库中的less区分
{
bool operator()(const T& t1, const T& t2)
{
return t1 < t2;
}
};
template<class T>
struct Greater
{
bool operator()(const T& t1, const T& t2)
{
return t1 > t2;
}
};
First, add a parameter to the template declaration:
template<class T,class Container = std::vector<T>,class Compare = Less<T>>
Then, change the size judgment part of upward adjustment and downward adjustment into functors
void AdjustDown(int parent)
{
Compare com;//仿函数使用前,实例化出一个对象
size_t child = 2 * parent + 1;
while (child < _con.size())
{
if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
{
child++;
}
if (com(_con[parent], _con[child]))
{
std::swap(_con[child], _con[parent]);
parent = child;
child = 2 * parent + 1;
}
else
{
break;
}
}
}
void AdjustUp(int child)
{
Compare com;//仿函数使用前,实例化出一个对象
int parent = (child - 1) / 2;
while (parent >= 0)
{
if (com(_con[parent], _con[child]))
{
std::swap(_con[parent], _con[child]);
child = parent;
parent = (child - 1) / 2;
}
else
{
break;
}
}
}