Priority queue priority_queue and the use of functors

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_queueit is a heap. Note: priority_queue is large by default

priority_queueIt is stored <queue>in the file. To use it, you only need to package queuethe 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:
insert image description here

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;
		}
	}
	
}

Guess you like

Origin blog.csdn.net/weixin_64116522/article/details/132689495