Article directory
-
Introduction to queue
-
Common interfaces for queue
-
Simulation implementation of queue
-
Introduction to priority_queue
-
Common interfaces of priority_queue
-
Simulation implementation of priority_queue
-
container adapter
-
Introduction to deque
-
Functor
1. Introduction to queue
- 1. A queue is a container adapter designed to operate in a FIFO context (first in, first out), where elements are inserted from one end of the container and elements are extracted from the other end.
- 2. The queue is implemented as a container adapter. The container adapter encapsulates a specific container class as its underlying container class. Queue provides a specific set of member functions to access its elements. Elements are put into the queue from the end of the queue and dequeued from the head.
- 3. The underlying container can be one of the standard container class templates or other specially designed container classes. The underlying container should support at least the following operations:
- empty: Check whether the queue is empty
- size: Returns the number of valid elements in the queue
- front: Returns a reference to the head element of the queue
- back: Returns a reference to the last element of the queue
- push_back: queue at the end of the queue
- pop_front: Dequeue at the head of the queue
- 4. The standard container classes deque and list meet these requirements. By default, if no container class is specified for queue instantiation, the standard container deque is used.
2. Common interfaces of queue
Function declaration interface descriptionqueue() constructs an empty queueempty() detects whether the queue is empty and returns true, otherwise it returns falsesize() returns the number of valid elements in the queuefront() returns a reference to the head element of the queueback() returns a reference to the last element of the queuepush() puts the element val into the queue at the end of the queuepop() dequeues the head element of the queueswap() exchanges the contents of two containers
Demonstration of related interfaces:
#include <iostream>
#include <queue>
int main()
{
std::queue<int> q;
q.push(1);
q.push(2);
q.push(3);
while (!q.empty()) {
std::cout << q.front() << " ";
q.pop();
}
return 0;
}
3. Simulation implementation of queue
#pragma once
namespace Queue
{
//这里默认是采用deque这种适配器来模拟栈
template<class T, class Contain = std::deque<T>>
class queue
{
public:
/*
queue()//这里不需要显示写构造函数,因为是自定义类型,直接调用默认构造函数就行
{}
*/
bool empty()
{
return _con.empty();
}
size_t size()const
{
return _con.size();
}
const T& front()const
{
return _con.front();
}
const T& back()const
{
return _con.back();
}
void push(const T& val)
{
_con.push_back(val);
}
void pop()
{
_con.pop_front();
}
void swap(queue<T, Contain>& q)
{
std::swap(_con, q._con);
}
private:
Contain _con;
};
}
4. Introduction to priority_queue
- 1. A priority queue is a container adapter whose first element is always the largest of the elements it contains according to strict weak ordering criteria.
- 2. This context is similar to a heap, where elements can be inserted at any time, and only the largest heap element (the top element in the priority queue) can be retrieved.
- 3. The priority queue is implemented as a container adapter. The container adapter encapsulates a specific container class as its underlying container class. The queue provides a specific set of member functions to access its elements. Elements are popped from the "tail" of a specific container, which is called the top of the priority queue.
- 4. The underlying container can be any standard container class template, or it can be other specifically designed container classes. The container should be accessible via random access iterators and support the following operations:
- empty(): Check whether the container is empty
- size(): Returns the number of valid elements in the container
- front(): Returns a reference to the first element in the container
- push_back(): Insert elements at the end of the container
- pop_back(): delete the element at the end of the container
- 5. The standard container classes vector and deque meet these needs. By default, vector is used if no container class is specified for a particular priority_queue class instantiation.
- 6. Need to support random access iterators so that the heap structure is always maintained internally. The container adapter does this automatically by automatically calling the algorithmic functions make_heap, push_heap, and pop_heap when needed.
5. Common interfaces of priority_queue
Function declaration interface descriptionpriority_queue()/priority_queue(first, last) constructs an empty priority queueempty() detects whether the priority queue is empty and returns true, otherwise it returns falsetop() returns the largest (smallest element) in the priority queue, that is, the top element of the heappush(x) inserts element x into the priority queuepop () deletes the largest (smallest) element in the priority queue, that is, the top element of the heap
1. By default, priority_queue is a large pile
#include <iostream>
#include <deque>
#include <queue>
#include "queue.h"
#include <vector>
#include <functional>
#include <functional> // greater算法的头文件
void TestPriorityQueue()
{
// 默认情况下,创建的是大堆,其底层按照小于号比较
std::vector<int> v{3,2,7,6,0,4,1,9,8,5};
std::priority_queue<int> q1;
for (auto& e : v)
q1.push(e);
std::cout << q1.top() <<std:: endl;
// 如果要创建小堆,将第三个模板参数换成greater比较方式
std::priority_queue<int, std::vector<int>, std::greater<int>> q2(v.begin(), v.end());
std::cout << q2.top() << std::endl;
}
int main()
{
TestPriorityQueue();
return 0;
}
2. If you put custom type data in priority_queue, the user needs to provide an overload of > or < in the custom type.
#include <iostream>
#include <deque>
#include <queue>
#include "queue.h"
#include <vector>
#include <functional>
#include <functional> // 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 std::ostream& operator<<(std::ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
private:
int _year;
int _month;
int _day;
};
void TestPriorityQueue()
{
// 大堆,需要用户在自定义类型中提供<的重载
std::priority_queue<Date> q1;
q1.push(Date(2018, 10, 29));
q1.push(Date(2018, 10, 28));
q1.push(Date(2018, 10, 30));
std::cout << q1.top() << std::endl;
// 如果要创建小堆,需要用户提供>的重载
std::priority_queue<Date, std::vector<Date>, std::greater<Date>> q2;
q2.push(Date(2018, 10, 29));
q2.push(Date(2018, 10, 28));
q2.push(Date(2018, 10, 30));
std::cout << q2.top() << std::endl;
}
int main()
{
TestPriorityQueue();
return 0;
}
6. Simulation implementation of priority_queue
#pragma once
namespace Priority_queue
{
template<class T>
struct Less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct Greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
template<class T,class Container=std::vector<T>,class Compare=Less<T>>
class priority_queue
{
public:
priority_queue()
{}
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last)
{
while (first != last) {
_con.push_back(*first);
first++;
int child = _con.size() - 1;
int parent = (child - 1) / 2;
for (int i = parent; i >= 0; i--) AdjustDown(i);
}
}
void AdjustUp(size_t child)
{
Compare com;
size_t parent = (child - 1) / 2;
while (child) {
if (com(_con[parent], _con[child])) {
std::swap(_con[child], _con[parent]);
child = parent;
parent = (child - 1) / 2;
}
else break;
}
}
void AdjustDown(size_t parent)
{
Compare com;
size_t child = parent * 2 + 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[parent], _con[child]);
parent = (child - 1) / 2;
child = parent * 2 + 1;
}
else break;
}
}
void push(const T& val)
{
_con.push_back(val);
AdjustUp(_con.size() - 1);
}
void pop()
{
std::swap(_con[0], _con[_con.size() - 1]);
_con.pop_back();
AdjustDown(0);
}
bool empty()
{
return _con.empty();
}
size_t size()
{
return _con.size();
}
const T& top()
{
return _con[0];
}
private:
Container _con;
};
}
7. Container adapter
1. The concept of adapter
2. The underlying structure of stack and queue in the STL standard library
8. Introduction to deque
1. Introduction to the principle of deque
2. Defects of deque
Compared with vector, the advantages of deque are :
- When inserting and deleting the head, there is no need to move elements, which is very efficient. Moreover, when expanding, there is no need to move a large number of elements, so its efficiency must be high.
- Compared with list, the bottom layer is continuous space, the space utilization is relatively high, and there is no need to store additional fields.
However, deque has a fatal flaw:
- It is not suitable for traversal, because when traversing, the iterator of deque needs to frequently detect whether it moves to the boundary of a certain small space, resulting in low efficiency. In sequential scenarios, frequent traversal may be required, so in practice, it is necessary When it comes to linear structures, vector and list are given priority in most cases. There are not many applications of deque. One application that can be seen so far is that STL uses it as the underlying data structure of stack and queue.
3. Why choose deque as the underlying default container for stack and queue ?
Stack is a special linear data structure with last-in-first-out function. Therefore, any linear structure with push_back() and pop_back() operations can be used as the underlying container of stack, such as vector and list; queue is a special first-in-first-out function. Linear data structures, as long as they have push_back and pop_front operations, can be used as the underlying container of queue, such as list. However, deque is selected as the underlying container by default for stack and queue in STL, mainly because:1. Stack and queue do not need to be traversed (so stack and queue do not have iterators), they only need to operate on one or both fixed ends.2. When the elements in the stack grow, deque is more efficient than vector (there is no need to move a large amount of data when expanding); when the elements in the queue grow, deque is not only efficient, but also has high memory usage. It combines the advantages of deque and perfectly avoids its shortcomings.
9. Functor
1. The concept of functor
Functor, also called function object, is actually a struct or class that overloads the () operator. Since the () operator is overloaded, using it is like calling a function, so it is called a "fake" function.
2. Examples of how to write functors
template<class T>
struct Less
{
bool operator()(const T& x, const T& y)
{
return x < y;
}
};
template<class T>
struct Greater
{
bool operator()(const T& x, const T& y)
{
return x > y;
}
};
3. Categories of functors
In the functional header file of C++, we have been provided with some functors that can be used directly.
1. Arithmetic Functor
1.plus calculates the sum of two numbers
transform(begin(a), end(a), begin(b), begin(a), plus<int>());
2.minus subtracts two numbers
transform(begin(a), end(a), begin(b), begin(a), minus<int>());
3.multiplies two numbers multiplied by each other
transform(begin(a), end(a), begin(b), begin(a), multiplies<int>());
4.divides divides two numbers
transform(begin(a), end(a), begin(b), begin(a), divides<int>());
5.modules modulo operation
transform(begin(a), end(a), begin(b), begin(a), modulus<int>());
6.negate opposite number
transform(begin(a), end(a), begin(a), negate<int>());
2. Relation functor
- 1.equal_to is equal
- 2.not_equal_to is not equal
- 3.greater greater than
- 4.less less than
- 5.greater_equal is greater than or equal to
- 6.less_equal is less than or equal to
3. Logic functor
- 1.logical_and binary, find &
- 2.logical_or binary, find |
- 3.logical_not one yuan, please!