priority_queue (priority queue)

insert image description here

1. Introduction and use of priority_queue

1.1 Introduction to priority_queue

In C++, priority_queue is a container adapter that provides constant-time maximum element lookup. It is usually implemented as a heap. A heap is a data structure where the largest (or smallest) element is always at the top. priority_queue is a template class defined in the header file. It has three template parameters: element type, container type, and comparison function type (optional). By default it uses std::vector as its underlying container .

1.2 The use of priority_queue

Member functions:
insert image description here

1.2.1 constructor (construction)

int main ()
{
    
    
  int myints[]= {
    
    10,60,50,20};

  priority_queue<int> q1;
  priority_queue<int> q2(myints,myints+4);
  priority_queue<int, vector<int>, greater<int>> q3(myints,myints+4);
  return 0;
}
  • q1 is empty.
  • q2 contains four integers defined for , with 60 (the highest) on top of it.
  • q3 has the same four integers, but since it uses instead of the default (ie ), it adds 10 as its top element for the new element. This new element is constructed in-place, passed as an argument to its constructor.

1.2.2 empty

In C++ STL, the empty() function is a predefined function used to check whether a collection is empty or not. Returns true (1) if the collection is empty, false (0) if the collection is not empty. The empty() function returns true for an empty container and false otherwise.

#include <iostream>
#include <queue>
using namespace std;

int main()
{
    
    
	int a[] = {
    
     3,6, 2,8,1 };
	priority_queue<int> q1;
	priority_queue<int> q2(a, a + 5);

	cout << "q1:" << q1.empty() << endl;
	cout << "q2:" << q2.empty() << endl;

	return 0;
}

insert image description here

1.2.3 size

The size() function in C++ STL returns the number of elements in the container.
insert image description here

1.2.4 top

The top() function is a member function of the priority_queue class in C++ STL, which is used to return the reference of the first element in the priority queue. When using the top() function, you need to pay attention to whether the priority queue is empty, otherwise undefined behavior will occur.

int main()
{
    
    
	int a[] = {
    
     3,6, 2,8,1 };
	priority_queue<int> q1;
	priority_queue<int> q2(a, a + 5);

	cout << "q1:" << q1.top() << endl;//报错
	cout << "q2:" << q2.top() << endl;//8

	return 0;
}

insert image description here

insert image description here

1.2.5 location

Construct and insert elements, add new elements. This new element is constructed in-place, passed as an argument to its constructor .

int main()
{
    
    
    priority_queue<string> mypq;

    mypq.emplace("orange");
    mypq.emplace("strawberry");
    mypq.emplace("apple");
    mypq.emplace("pear");

    cout << "mypq contains:";
    while (!mypq.empty())
    {
    
    
        cout << ' ' << mypq.top();
        mypq.pop();
    }
    cout << '\n';

    return 0;
}

insert image description here

1.2.6 push、pop、swap

The push() function is used to insert new elements into the priority_queue and maintain the order of the queue.
The pop() function in priority_queue is used to remove the first element in the queue, which is the largest element.
The swap() function is used to exchange the elements of two priority_queues.

int main()
{
    
    
	priority_queue<int> q1;
	q1.push(1);
	q1.push(2);
	q1.push(3);
	priority_queue<int> q2;
	q2.push(4);
	q2.push(5);
	q2.push(6);
	swap(q1, q2);

	cout << "q1: ";
	while (!q1.empty())
	{
    
    
		cout << q1.top() << ' ';
		q1.pop();
	}
	cout << endl;

	cout << "q2: ";
	while (!q2.empty())
	{
    
    
		cout << q2.top() << ' ';
		q2.pop();
	}
	cout << endl;
	return 0;
}

insert image description here

1.3 The Kth largest element in the array

insert image description here

The Kth largest element in the array
The first method: Sort the array nums, and then find the kth largest number. It should be noted that the time complexity of general sorting in the algorithm is greater than O(N), that is, the counting sort is close to O(N).
insert image description here

The second method: create a priority queue with N elements, and find the k-th largest element, then delete the first k-1 elements in the priority queue, and the top of the last queue is the requested element

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k)
    {
    
    
        priority_queue<int> deq(nums.begin(), nums.end());
        while(--k)
        {
    
    
            deq.pop();
        }
        return deq.top();
    }
};

insert image description here

The third method: create a priority queue (small heap) with K elements, then traverse nums, enter the queue with large elements, and finally the top of the queue is the requested element

class Solution {
    
    
public:
    int findKthLargest(vector<int>& nums, int k)
    {
    
    
        priority_queue<int, vector<int>, greater<int>> deq(nums.begin(), nums.begin() + k);
        for (size_t i = k; i < nums.size(); ++i)
        {
    
    
            if (nums[i] > deq.top())
            {
    
    
                deq.pop();
                deq.push(nums[i]);
            }
        }
        return deq.top();
    }
};

insert image description here

2. In-depth analysis and simulation implementation of priority_queue

priority_queue has three template parameters: element type, container type, and comparison function type (optional). By default, it uses std::vector as its underlying container , and does not require iterators, so the implementation is relatively simple.

namespace k
{
    
    
	template<class T, class Container = vector<T>>
	class priority_deque
	{
    
    
	public:
		size_t size()
		{
    
    
			return _con.size();
		}
		void push(const T& x)
		{
    
    
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}
		void pop()
		{
    
    
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}
		const T& top()
		{
    
    
			return _con[0];
		}
		bool empty()
		{
    
    
			return _con.empty();
		}
	protected:
		void adjust_up(size_t child)
		{
    
    
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
    
    
				if (_con[parent] <  _con[child])
				{
    
    
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}
			}
		}
		void adjust_down(size_t parent)
		{
    
    
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
    
    
				if (child + 1 < _con.size() && _con[child] < _con[child + 1])
				{
    
    
					++child;
				}
				if (_con[parent] < _con[child])
				{
    
    
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
    
    
					break;
				}
			}
		}
	private:
		Container _con;
	};
}

The above is the implementation of priority_queue, in which the upward adjustment (adjust_up) and downward adjustment (asjust_down) are adjusted to a large pile, which is hard-coded. If a small pile is required, how to do it? This is the detail of the template. As long as you define a comparison method, you can solve it.

namespace k
{
    
    
	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 = vector<T>, class Compare = less<T>>
	class priority_deque
	{
    
    
	public:
		size_t size()
		{
    
    
			return _con.size();
		}
		void push(const T& x)
		{
    
    
			_con.push_back(x);
			adjust_up(_con.size() - 1);
		}
		void pop()
		{
    
    
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			adjust_down(0);
		}
		const T& top()
		{
    
    
			return _con[0];
		}
		bool empty()
		{
    
    
			return _con.empty();
		}
	protected:
		void adjust_up(size_t child)
		{
    
    
			Compare com;
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
    
    
				if (com(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
    
    
					break;
				}
			}
		}
		void adjust_down(size_t parent)
		{
    
    
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
    
    
				Compare com;
				if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
				{
    
    
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
    
    
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
    
    
					break;
				}
			}
		}
	private:
		Container _con;
	};
}

Define two functors as above, and then template import can be implemented.

Guess you like

Origin blog.csdn.net/weixin_68278653/article/details/131015181