One of the first articles of C++ teaches you queue and priority_queue (understanding the use and simulation implementation)

insert image description here

what is queue

insert image description here

  1. A queue is a container adapter designed to FIFOoperate in a context (first in first out) where elements are inserted from one end of the container and extracted from the other.
  2. A queue is implemented as a container adapter, which encapsulates a specific container class as its underlying container class and queueprovides a specific set of member functions to access its elements. Elements enter the queue from the tail and dequeue from the head.
  3. The underlying container can be one of the standard container class templates, or another specially designed container class. The underlying container shall support at least the following operations:

empty : check whether the queue is empty
size : return the number of valid elements in the queue
front : return the reference of the element at the head of the queue
back : return the reference of the element at the end of the queue
push_back : enter the queue at the end of the queue
pop_front : exit the queue at the head of the queue

  1. The standard container classes dequesum listfulfill these requirements. By default, if no queuecontainer class is specified for instantiation, the standard container is used deque.

The use of queue

1.queue constructor

insert image description here

initialize (1): Initialize the queue with an existing container.

explicit queue(const container_type& ctnr);
This constructor takes an existing container ctnrand initializes the queue with its contents.

Example:

std::deque<int> myDeque = {
    
    1, 2, 3};
std::queue<int> myQueue(myDeque); // 使用已存在的 deque 初始化队列

move-initialize (2): Use the existing container to initialize the queue, and empty the original container after initialization.

explicit queue(container_type&& ctnr = container_type());
This constructor takes a container of rvalue references ctnr, uses it to initialize the queue, and empties it after initialization ctnr.

Example:

std::deque<int> myDeque = {
    
    1, 2, 3};
std::queue<int> myQueue(std::move(myDeque)); // 使用已存在的 deque 初始化队列,并清空 myDeque

allocator (3): Initialize the queue with the custom allocator Alloc.

template <class Alloc> explicit queue(const Alloc& alloc);
This constructor accepts a custom allocator type Allocthat is used to allocate memory on initialization.

Example:

std::allocator<int> myAllocator;
std::queue<int> myQueue(myAllocator); // 使用自定义分配器初始化队列

init + allocator (4): Initialize the queue using an existing container and a custom allocator.

template <class Alloc> queue(const container_type& ctnr, const Alloc& alloc);
This constructor accepts an existing container ctnrand a custom allocator alloc, which is used to specify the underlying container and allocator at initialization time.

Example:

std::deque<int> myDeque = {
    
    1, 2, 3};
std::allocator<int> myAllocator;
std::queue<int> myQueue(myDeque, myAllocator); // 使用已存在的 deque 和自定义分配器初始化队列

move-init + allocator (5): Initialize the queue with an existing container and a custom allocator, and empty the original container after initialization.

template <class Alloc> queue(container_type&& ctnr, const Alloc& alloc);
Similar to (2), this constructor accepts a container of rvalue references ctnrand a custom allocator allocthat is used to initialize the queue and is emptied after initialization ctnr.

Example:

std::deque<int> myDeque = {
    
    1, 2, 3};
std::allocator<int> myAllocator;
std::queue<int> myQueue(std::move(myDeque), myAllocator); // 使用已存在的 deque 和自定义分配器初始化队列,并清空 myDeque

copy + allocator (6): Copy an existing queue and initialize a new queue with a custom allocator.

template <class Alloc> queue(const queue& x, const Alloc& alloc);
This constructor accepts an existing queue xand a custom allocator to copy elements from to the new queue allocon initialization .x

Example:

std::queue<int> originalQueue;
// 添加一些元素到 originalQueue
std::allocator<int> myAllocator;
std::queue<int> myQueue(originalQueue, myAllocator); // 复制已存在队列并使用自定义分配器初始化新队列

move + allocator (7): Move an existing queue and initialize a new queue with a custom allocator.

template <class Alloc> queue(queue&& x, const Alloc& alloc);
Similar to (5), this constructor accepts a queue of rvalue references xand a custom allocator allocfor initializing a new queue and xmoving elements from it.

Example:

std::queue<int> originalQueue;
// 添加一些元素到 originalQueue
std::allocator<int> myAllocator;
std::queue<int> myQueue(std::move(originalQueue), myAllocator); // 移动已存在队列并使用自定义分配器初始化新队列

2.empty()

bool empty() constIs std::queueone of the member functions of the class, used to determine whether the queue is empty. This function does not modify the contents of the queue, so it is declared as const, indicating that it will not modify the object.

return value:

Returns if the queue is empty true.
Returns if the queue is not empty false.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    if (myQueue.empty()) {
    
    
        std::cout << "Queue is empty." << std::endl;
    } else {
    
    
        std::cout << "Queue is not empty." << std::endl;
    }
    
    myQueue.push(42);
    
    if (myQueue.empty()) {
    
    
        std::cout << "Queue is empty." << std::endl;
    } else {
    
    
        std::cout << "Queue is not empty." << std::endl;
    }
    
    return 0;
}

In this example, an empty queue is created first myQueue, and then empty()the function is used to determine whether the queue is empty. After adding an element, call empty()the function again to verify the state of the queue. From the output, you can see that the queue is empty when it has no elements, and not empty after adding an element.

3.size()

size_type size() constIs std::queueone of the member functions of the class, used to get the number of elements in the queue. This function does not modify the contents of the queue, so it is declared as const, indicating that it will not modify the object.

return value:

Returns the current number of elements (size) in the queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    std::cout << "Initial size: " << myQueue.size() << std::endl;
    
    myQueue.push(42);
    myQueue.push(20);
    myQueue.push(10);
    
    std::cout << "Updated size: " << myQueue.size() << std::endl;
    
    return 0;
}

In this example, first create an empty queue myQueue, then use size()the function to get the initial size of the queue. Then add three elements to the queue, and call size()the function again to get the updated queue size. From the output, you can see that the queue changed size after adding elements.

4.front()

reference& front()and const_reference& front() constis std::queueone of the member functions of the class used to access the front (head) element of the queue. These functions do not modify the contents of the queue, so are declared as constor return constant references, indicating that they do not modify the object.

referenceand const_referenceare Tthe reference type and constant reference type of the template parameter, which are used to represent the type and constant reference type of the elements in the queue respectively. The returned reference allows you to access the first element of the queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    myQueue.push(42);
    myQueue.push(20);
    
    int& firstElement = myQueue.front();
    const int& constFirstElement = myQueue.front();
    
    std::cout << "First element: " << firstElement << std::endl;
    std::cout << "Constant first element: " << constFirstElement << std::endl;
    
    return 0;
}

In this example, a queue is first created myQueue, and then two elements are added. front()Get the first element of the queue via the function and store it in a non-const reference firstElementand a const reference constFirstElement. From the output, you can see that both references can access the value of the first element of the queue.

5.back();

reference& back()and const_reference& back() constis std::queueone of the member functions of the class used to access the last (tail) element of the queue. These functions do not modify the contents of the queue, so are declared as constor return constant references, indicating that they do not modify the object.

referenceand const_referenceare Tthe reference type and constant reference type of the template parameter, which are used to represent the type and constant reference type of the elements in the queue respectively. The returned reference allows you to access the last element of the queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    myQueue.push(42);
    myQueue.push(20);
    
    int& lastElement = myQueue.back();
    const int& constLastElement = myQueue.back();
    
    std::cout << "Last element: " << lastElement << std::endl;
    std::cout << "Constant last element: " << constLastElement << std::endl;
    
    return 0;
}

In this example, a queue is first created myQueue, and then two elements are added. Get the last element of the queue via back()the function and store it in a non-const reference lastElementand a const reference constLastElement. From the output, you can see that both references can access the value of the last element of the queue.

6.push

void push (const value_type& val)and void push (value_type&& val)is one of the member functions of the std::queue class and is used to add elements to the end of the queue.

const value_type& valIs a constant reference used to pass the element that needs to be added to the queue.
value_type&& valis an rvalue reference used to pass the element that needs to be added to the queue, typically used to support move semantics.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    myQueue.push(42); // 使用右值
    int value = 20;
    myQueue.push(value); // 使用左值
    
    return 0;
}

In this example, a queue is first created myQueue, and then push()two values ​​of different types are added to the queue using the function. The first push()uses rvalues 42, the second push()uses lvalue variables value. The queue will hold elements in the order they were added.

7.place

template <class... Args> void emplace (Args&&... args)Is std::queueone of the member functions of the class used to construct a new element directly at the end of the queue via the constructor parameter list.

Args... argsis a template parameter pack that represents the list of parameters passed to the constructor.
Use emplace()the function to avoid extra object copying or moving operations, and directly construct the object inside the container.

Example usage:

#include <iostream>
#include <queue>

class MyObject {
    
    
public:
    MyObject(int value) : m_value(value) {
    
    
        std::cout << "Constructed: " << m_value << std::endl;
    }
    
    ~MyObject() {
    
    
        std::cout << "Destructed: " << m_value << std::endl;
    }

private:
    int m_value;
};

int main() {
    
    
    std::queue<MyObject> myQueue;
    
    myQueue.emplace(42); // 使用右值参数构造
    myQueue.emplace(20); // 使用右值参数构造
    
    return 0;
}

In this example, a queue is first created myQueue, and then emplace()the function is used to construct two objects directly at the end of the queue via rvalue parameters MyObject. Due to the use of emplace(), the object will be constructed directly on the queue without additional copy or move operations occurring.

8.pop()

void pop()Is std::queueone of the member functions of the class, used to remove the element at the head of the queue from the queue.

Calling pop()the function removes the first element in the queue and moves the remaining elements in the queue forward to fill the place of the removed element.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> myQueue;
    
    myQueue.push(42);
    myQueue.push(20);
    myQueue.push(10);
    
    std::cout << "Size before pop: " << myQueue.size() << std::endl;
    
    myQueue.pop();
    
    std::cout << "Size after pop: " << myQueue.size() << std::endl;
    
    return 0;
}

In this example, first create a queue myQueue, and then use push()the function to add three elements to the queue. By calling pop()the function, the element at the head of the queue is 42removed. From the output, you can see that pop()the size of the queue is reduced after calling .

9.swap

void swap(queue& x) noexceptIs std::queueone of the member functions of the class, used to exchange xthe contents of the current queue and another queue.

x: Another queue to exchange with the current queue.
noexcept: This function is declared as not throwing an exception, which means that no exception will be thrown during the exchange.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::queue<int> queue1;
    std::queue<int> queue2;

    queue1.push(42);
    queue1.push(20);
    
    queue2.push(10);

    std::cout << "Before swap:" << std::endl;
    std::cout << "Queue 1 front: " << queue1.front() << std::endl;
    std::cout << "Queue 2 front: " << queue2.front() << std::endl;

    queue1.swap(queue2);

    std::cout << "After swap:" << std::endl;
    std::cout << "Queue 1 front: " << queue1.front() << std::endl;
    std::cout << "Queue 2 front: " << queue2.front() << std::endl;

    return 0;
}

In this example, first create two queues queue1and queue2and add elements to them respectively. By calling swap()the function, the contents of the queue are swapped. From the output, you can see that after the swap, the contents of the queue were also swapped.

Queue simulation implementation

#pragma once
#include <deque>

namespace xzq
{
    
    
	template<class T, class Container = deque<T>>
	class queue
	{
    
    
	public:
		void push(const T& x)
		{
    
    
			_con.push_back(x);
		}

		void pop()
		{
    
    
			_con.pop_front();
		}

		T& back()
		{
    
    
			return _con.back();
		}

		T& front()
		{
    
    
			return _con.front();
		}

		const T& back() const
		{
    
    
			return _con.back();
		}

		const T& front() const
		{
    
    
			return _con.front();
		}


		bool empty()  const
		{
    
    
			return _con.empty();
		}

		size_t size() const
		{
    
    
			return _con.size();
		}
	private:
		Container _con;
	};
}

First, this queue class takes a Containertemplate parameter called with a default value of std::deque<T>. This allows you to specify the underlying container type when creating the queue object, if not specified, the default will be used std::deque.

push(const T& x)function is used to xadd an element to the end of the queue, it actually calls push_back()the method of the underlying container.

pop()function is used to remove the element at the head of the queue, it actually calls pop_front()the method of the underlying container.

back()The function returns a reference to the last element in the queue, which can be used to access the end element of the queue.

front()The function returns a reference to the first element in the queue, which is used to access the head element of the queue.

empty()function to check if the queue is empty, it actually calls empty()the method of the underlying container.

size()function is used to get the number of elements in the queue, it actually calls size()the method of the underlying container.

The private member _conis the underlying container object used to store the elements of the queue.

Using this simulated queue class, you can choose different underlying container types (the default is std::deque), and call the methods of the class to simulate the basic operations of the queue, such as adding elements, removing elements, accessing elements, judging whether they are empty, etc. This implementation allows you to use queues more flexibly through templates to adapt to different data types and underlying containers.

What is priority_queue

insert image description here

  1. A priority queue is a container adapter whose first element is always the largest of its contained elements 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 one at the top in the priority queue) can be retrieved.
  3. The priority queue is implemented as a container adapter, which encapsulates a specific container class as its underlying container class and queueprovides a set of specific member functions to access its elements. Elements are popped from the "tail" of a particular container, which is called the top of the priority queue.
  4. The underlying container can be any standard container class template, or any other container class of a specific design. Containers should be accessible via random access iterators and support the following operations:

empty() : Check whether the container is empty
size() : Return the number of valid elements in the container
front() : Return the reference to the first element in the container
push_back() : Insert an element at the end of the container
pop_back() : Delete the element at the end of the container

  1. Standard container classes vectorand dequefulfill these needs. By default, if no priority_queuecontainer class is specified for a particular class instantiation, it is used vector.
  2. Random access iterators need to be supported so that the heap structure is always maintained internally. The container adapter does this automatically by automatically calling algorithmic functions make_heap, push_heapand when needed .pop_heap

The use of priority_queue

1. priority_queue constructor

insert image description here
std::priority_queueis C++ STLa priority queue container provided by , which is implemented based on a heap, allowing you to add and remove elements in a specific order.

The following are explanations and examples of these constructors:

priority_queue(const Compare& comp, const Container& ctnr): Constructs a priority queue, using the given comparison function compand underlying container ctnr.

priority_queue(InputIterator first, InputIterator last, const Compare& comp, const Container& ctnr): Constructs a priority queue using the elements in the iterator range [first, last)with the given comparison function compand underlying container ctnr.

explicit priority_queue(const Compare& comp = Compare(), Container&& ctnr = Container()): Constructs a priority queue, using the given comparison function compand the underlying container passed with move semantics ctnr.

template <class InputIterator> priority_queue(InputIterator first, InputIterator last, const Compare& comp, Container&& ctnr = Container()): Constructs a priority queue using the elements in the iterator range and the underlying container passed [first, last)with the given comparison function and move semantics .compctnr

AllocatorVersion: These constructors use a different allocator allocatorto construct the priority queue.

Example:

#include <iostream>
#include <queue>
#include <vector>

int main() {
    
    
    // 使用默认底层容器 std::vector,以默认比较函数(最大堆)构造优先队列
    std::priority_queue<int> maxHeap;

    // 使用自定义比较函数(最小堆)和底层容器 std::deque 构造优先队列
    auto compare = [](int a, int b) {
    
     return a > b; };
    std::deque<int> container = {
    
    5, 3, 8, 1, 9};
    std::priority_queue<int, std::deque<int>, decltype(compare)> minHeap(compare, container);

    // 使用迭代器范围构造优先队列
    std::vector<int> elements = {
    
    7, 2, 4, 6, 0};
    std::priority_queue<int, std::vector<int>> iteratorQueue(elements.begin(), elements.end());

    // 输出优先队列中的元素
    while (!iteratorQueue.empty()) {
    
    
        std::cout << iteratorQueue.top() << " ";
        iteratorQueue.pop();
    }

    return 0;
}

In this example, we demonstrate the usage of different constructors. First, a max-heap priority queue is constructed using the default constructor. std::dequeThen, we construct a min-heap priority queue using a custom comparison function and the underlying container . Finally, a priority queue is constructed using the iterator range. Based on the output, you can see that the priority queue outputs elements in a different order.

1.1 Template parameter Compare

In the class, the function object used to compare elements is specified std::priority_queuethrough the template parameter to affect the sorting method of the heap. Is aCompareComparefunctor, which defines how elements are compared. Depending on the Comparepriority queue can become a large heap (max heap) or a small heap (min heap).

Default std::less<T>(large heap):
std::less is a function object that overloads operator() for comparing two elements. It returns a boolean indicating whether the first argument is less than the second argument. By default, if the Compare parameter is not provided, the priority queue uses std::less as the comparison function object, that is, a large heap. This means that in a large heap, the value of the parent node is always greater than or equal to the value of the child node.

std::greater<T>(Small heap):
std::greater<T> is another function object, which is overloaded operator()and used to compare two elements. Unlike std::less<T>, std::greater<T>returns a boolean indicating whether the first argument is greater than the second argument. If you std::greater<T>pass to priority_queue, it will construct a small heap. In a small heap, the value of the parent node is always less than or equal to the value of the child node.

The following is sample code that demonstrates how to use different comparison function objects to create large and small heaps:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap; // 默认大堆

    std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap; // 小堆

    maxHeap.push(5);
    maxHeap.push(3);
    maxHeap.push(8);

    minHeap.push(5);
    minHeap.push(3);
    minHeap.push(8);

    std::cout << "Max Heap (Top element): " << maxHeap.top() << std::endl;
    std::cout << "Min Heap (Top element): " << minHeap.top() << std::endl;

    return 0;
}

In this example, we create a large heap and a small heap separately. Through top()the function, we can see that the top element of the large pile is the largest, and the top element of the small pile is the smallest. This reflects the effect of different comparison function objects.

1.2 What is a functor?

Functor is a class object that overloads the function call operator operator()so that the object can be called like a function. It is actually a function object, which can have its own member variables and operations, and can be used similar to ordinary functions.

One of the main advantages of using functors is that you can encapsulate the behavior and state of a function in an object, making your code more readable and maintainable. Functors can be used in a variety of situations, including standard algorithms, STLcontainers, and other places where functional operations are required.

A functor class usually requires at least the implementation operator(), which can have different parameters and return types, depending on the purpose of the functor. Here is a simple example:

#include <iostream>

class Adder {
    
    
public:
    Adder(int value) : value_(value) {
    
    }

    int operator()(int x) {
    
    
        return x + value_;
    }

private:
    int value_;
};

int main() {
    
    
    Adder addFive(5);
    Adder addTen(10);

    int result1 = addFive(7); // 调用仿函数 addFive
    int result2 = addTen(7);  // 调用仿函数 addTen

    std::cout << "Result 1: " << result1 << std::endl;
    std::cout << "Result 2: " << result2 << std::endl;

    return 0;
}

In this example, Adderthe class is defined as a functor that takes an integer value and adds that value to the passed argument when called. In main()the function, we create two Adderobjects addFiveand addTenthen perform the addition operation by calling them.

In short, a functor is a function object that enables an object to be called like a function, so that the behavior and state of the function can be encapsulated in an object. This is very useful when writing more flexible and readable code.

2.empty()

bool empty() constIs std::priority_queueone of the member functions of the class, used to check whether the priority queue is empty.

empty()The function returns a boolean indicating whether the priority queue is empty.
constThe modifier indicates that this function will not modify the contents of the priority queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    if (maxHeap.empty()) {
    
    
        std::cout << "The priority queue is empty." << std::endl;
    } else {
    
    
        std::cout << "The priority queue is not empty." << std::endl;
    }

    maxHeap.push(42);

    if (maxHeap.empty()) {
    
    
        std::cout << "The priority queue is empty." << std::endl;
    } else {
    
    
        std::cout << "The priority queue is not empty." << std::endl;
    }

    return 0;
}

In this example, we first create an empty std::priority_queueobject maxHeap, then use empty()the function to check whether it is empty. From the output, you can see that the priority queue is no longer empty after adding an element.

3.size()

size_type size() constIs std::priority_queueone of the member functions of the class, used to get the number of elements in the priority queue.

size()The function returns an unsigned integer size_typerepresenting the number of elements in the priority queue.
constThe modifier indicates that this function will not modify the contents of the priority queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    maxHeap.push(42);
    maxHeap.push(20);
    maxHeap.push(10);

    std::cout << "Size of the priority queue: " << maxHeap.size() << std::endl;

    return 0;
}

In this example, we create a big heap priority queue with three elements maxHeap, and then use size()the function to get the number of elements in the queue. From the output, you can see that there are three elements in the queue.

4.top()

const_reference top() constIs std::priority_queueone of the member functions of the class, which is used to get the top element of the priority queue (the largest element or the smallest element, depending on the type of heap), but does not change the content of the queue.

top()The function returns a constant reference to the top element of the queue const_reference, either the largest element or the smallest element.
constThe modifier indicates that this function will not modify the contents of the priority queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    maxHeap.push(42);
    maxHeap.push(20);
    maxHeap.push(10);

    std::cout << "Top element: " << maxHeap.top() << std::endl;

    return 0;
}

In this example, we create a big heap priority queue maxHeapand add three elements. Using top()the function, we get the top element (largest element) in the queue. According to the output, you can see that the value of the top element is 42.

5.push

void push(const value_type& val)and void push(value_type&& val)is std::priority_queueone of the member functions of the class used to add new elements to the priority queue.

push(const value_type& val) Takes a constant reference valand copies a new element into the priority queue.
push(value_type&& val)Takes an rvalue reference val, using move semantics to move a new element into the priority queue.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    maxHeap.push(42); // 使用 push(const value_type& val)
    maxHeap.push(20); // 使用 push(const value_type& val)
    maxHeap.push(10); // 使用 push(const value_type& val)

    std::cout << "Top element: " << maxHeap.top() << std::endl;

    return 0;
}

In this example, we add elements to the bulk priority queue using two different methods maxHeap. The first is to use push(const value_type& val), the second is to use push(value_type&& val). Both ways add elements to the queue. According to the output, you can see that the value of the top element is 42because the priority queue automatically puts the largest (or smallest) element at the top.

6.place

template <class... Args> void emplace(Args&&... args)Is std::priority_queueone of the member functions of the class, used to insert a new element in the priority queue by means of in-place construction.

emplace()Functions use parameter packs parameter packto accept the parameters needed to construct elements.
It can be inserted in existing elements, while avoiding additional copy or move operations, improving efficiency.
This function uses perfect forwarding to pass arguments to accommodate different types of constructors.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    maxHeap.emplace(42); // 插入一个新元素

    int value = 20;
    maxHeap.emplace(value); // 插入一个新元素,使用拷贝构造函数

    maxHeap.emplace(10); // 插入一个新元素

    std::cout << "Top element: " << maxHeap.top() << std::endl;

    return 0;
}

In this example, we use emplace()the function to insert new elements into the bulk priority queue with in-place construction maxHeap. While inserting, we can pass the parameters needed to construct the element. This function automatically calls the appropriate constructor to insert new elements into the heap. According to the output, you can see that the value of the top element is 42.

7.pop()

void pop()Is std::priority_queueone of the member functions of the class that removes the top element of the priority queue (either the largest element or the smallest element, depending on the type of heap).

pop()The function removes the top element of the priority queue, while readjusting the heap to maintain the properties of the heap.
Note that you need to ensure that the queue is not empty before calling this function, otherwise undefined behavior will occur.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap;

    maxHeap.push(42);
    maxHeap.push(20);
    maxHeap.push(10);

    std::cout << "Top element before pop: " << maxHeap.top() << std::endl;

    maxHeap.pop();

    std::cout << "Top element after pop: " << maxHeap.top() << std::endl;

    return 0;
}

In this example, we first create a big heap priority queue maxHeap, then push()add three elements using the function. With top()the function, we get the top element of the queue. Then, we use pop()the function to remove the top element, and top()get the new top element through the function again. From the output, you can see that after removing an element, the top element of the queue becomes 20.

8.swap

void swap(priority_queue& x) noexceptIs std::priority_queueone of the member functions of the class, used to exchange the contents of two priority queues.

swap()The function is used to exchange the content between the calling object and the passed parameter x.
This operation causes the contents of the two priority queues to be swapped, but does not change their comparison functions or other properties.
noexceptkeyword indicates that this function will not throw exceptions.

Example usage:

#include <iostream>
#include <queue>

int main() {
    
    
    std::priority_queue<int> maxHeap1;
    std::priority_queue<int> maxHeap2;

    maxHeap1.push(42);
    maxHeap1.push(20);

    maxHeap2.push(10);
    maxHeap2.push(30);

    std::cout << "Max Heap 1 (Top element before swap): " << maxHeap1.top() << std::endl;
    std::cout << "Max Heap 2 (Top element before swap): " << maxHeap2.top() << std::endl;

    maxHeap1.swap(maxHeap2);

    std::cout << "Max Heap 1 (Top element after swap): " << maxHeap1.top() << std::endl;
    std::cout << "Max Heap 2 (Top element after swap): " << maxHeap2.top() << std::endl;

    return 0;
}

In this example, we create two big heaps, priority queue maxHeap1and maxHeap2, and add different elements to each. With top()the function, we get the top elements of the two queues. Then using swap()the function, we swap the contents of the two queues. From the output, you can see that after the swap, the contents of the two queues are swapped.

Simulate the implementation of priority_queue

#pragma once

namespace xzq
{
    
    
	// Compare进行比较的仿函数 less->大堆
	// Compare进行比较的仿函数 greater->小堆
	template<class T, class Container = vector<T>, class Compare = std::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;
			}

			for (int i = (_con.size()-1-1)/2; i >= 0; --i)
			{
    
    
				adjust_down(i);
			}
		}

		void adjust_up(size_t child)
		{
    
    
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
    
    
				if (com(_con[parent], _con[child]))
				{
    
    
					std::swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}
			}
		}

		void push(const T& x)
		{
    
    
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}

		void adjust_down(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[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
    
    
					break;
				}
			}
		}

		void pop()
		{
    
    
			std::swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();

			adjust_down(0);
		}

		const T& top()
		{
    
    
			return _con[0];
		}

		bool empty()  const
		{
    
    
			return _con.empty();
		}

		size_t size() const
		{
    
    
			return _con.size();
		}

	private:
		Container _con;
	};
}

Namespace xzq:
This code is located in the namespace xzq, which is a custom namespace used to encapsulate related classes, functions, etc. to avoid naming conflicts with other codes.

Template class priority_queue:
This is a template class that represents the implementation of a priority queue. It takes three template arguments: T(element type), Container(underlying container type, defaults to std::vector<T>), and Compare(functor for comparing elements, defaults to std::less<T>)

Compareis a template parameter used to compare elements. It is a functor that can be std::less<T>(default) or std::greater<T>, depending on the user-supplied priority queue type. CompareThe functor is used to determine the ordering of elements in the heap, thus determining whether it is a max heap or a min heap. In priority_queueeach member function of the class, Comparethe comparison of elements is performed by calling the functor, thereby realizing the operation of inserting and adjusting the heap.

Constructor priority_queue():
This is a default constructor that does not need to use Comparefunctors for comparison.

Constructor template priority_queue(InputIterator first, InputIterator last):
Inside the constructor, use Comparethe functor to perform comparison operations to determine the order of the elements. After adding elements, adjust_downthe heap is built by calling the function.

Member function adjust_up(size_t child):
In the float operation, use Comparethe functor to perform comparison to determine whether to exchange the positions of the parent and child nodes, thus maintaining the nature of the heap.

Member function push(const T& x):
In the insert operation, first add the new element to the underlying container _con, and then adjust_upperform the floating operation by calling the function to ensure that the new element is in a proper position.

Member function adjust_down(size_t parent):
In the sinking operation, Comparethe functor is used to perform a comparison to determine whether the position of the parent and child nodes needs to be exchanged, thereby maintaining the nature of the heap.

Member function pop():
In the delete operation, first exchange the top element with the last element of the bottom container, and then perform the sinking operation by calling the adjust_down function to ensure the nature of the heap.

Member function const T& top(): Get the top element of the priority queue
by returning ._con[0]

epilogue

Interested friends can pay attention to the author, if you think the content is good, please give a one-click triple link, you crab crab! ! !
It is not easy to make, please point out if there are any inaccuracies
Thank you for your visit, UU watching is the motivation for me to persevere.
With the catalyst of time, let us all become better people from each other! ! !

Guess you like

Origin blog.csdn.net/kingxzq/article/details/132256833